Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Blameable;
/**
* Blameable trait.
*
* Should be used inside entity where you need to track which user created or updated it
*/
trait Blameable
{
use BlameableProperties,
BlameableMethods
;
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Blameable;
/**
* Blameable trait.
*
* Should be used inside entity where you need to track which user created or updated it
*/
trait BlameableMethods
{
/**
* @param mixed the user representation
* @return $this
*/
public function setCreatedBy($user)
{
$this->createdBy = $user;
return $this;
}
/**
* @param mixed the user representation
* @return $this
*/
public function setUpdatedBy($user)
{
$this->updatedBy = $user;
return $this;
}
/**
* @param mixed the user representation
* @return $this
*/
public function setDeletedBy($user)
{
$this->deletedBy = $user;
return $this;
}
/**
* @return mixed the user who created entity
*/
public function getCreatedBy()
{
return $this->createdBy;
}
/**
* @return mixed the user who last updated entity
*/
public function getUpdatedBy()
{
return $this->updatedBy;
}
/**
* @return mixed the user who removed entity
*/
public function getDeletedBy()
{
return $this->deletedBy;
}
public function isBlameable()
{
return true;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Blameable;
/**
* Blameable trait.
*
* Should be used inside entity where you need to track which user created or updated it
*/
trait BlameableProperties
{
/**
* Will be mapped to either string or user entity
* by BlameableSubscriber
*/
protected $createdBy;
/**
* Will be mapped to either string or user entity
* by BlameableSubscriber
*/
protected $updatedBy;
/**
* Will be mapped to either string or user entity
* by BlameableSubscriber
*/
protected $deletedBy;
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Geocodable;
use Knp\DoctrineBehaviors\ORM\Geocodable\Type\Point;
/**
* Geocodable trait.
*
* Should be used inside entity where you need to manipulate geographical information
*/
trait Geocodable
{
use GeocodableProperties,
GeocodableMethods
;
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Geocodable;
use Knp\DoctrineBehaviors\ORM\Geocodable\Type\Point;
/**
* Geocodable trait.
*
* Should be used inside entity where you need to manipulate geographical information
*/
trait GeocodableMethods
{
/**
* Get location.
*
* @return Point.
*/
public function getLocation()
{
return $this->location;
}
/**
* Set location.
*
* @param Point|null $location the value to set.
*
* @return $this
*/
public function setLocation(Point $location = null)
{
$this->location = $location;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Geocodable;
use Knp\DoctrineBehaviors\ORM\Geocodable\Type\Point;
/**
* Geocodable trait.
*
* Should be used inside entity where you need to manipulate geographical information
*/
trait GeocodableProperties
{
protected $location;
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Loggable;
/**
* Loggable trait.
*
* Should be used inside entity where you need to track modifications log
*/
trait Loggable
{
/**
* @return string some log informations
*/
public function getUpdateLogMessage(array $changeSets = [])
{
$message = [];
foreach ($changeSets as $property => $changeSet) {
for ($i = 0, $s = sizeof($changeSet); $i < $s; $i++) {
if ($changeSet[$i] instanceof \DateTime) {
$changeSet[$i] = $changeSet[$i]->format("Y-m-d H:i:s.u");
}
}
if ($changeSet[0] != $changeSet[1]) {
$message[] = sprintf(
'%s #%d : property "%s" changed from "%s" to "%s"',
__CLASS__,
$this->getId(),
$property,
!is_array($changeSet[0]) ? $changeSet[0] : "an array",
!is_array($changeSet[1]) ? $changeSet[1] : "an array"
);
}
}
return implode("\n", $message);
}
public function getCreateLogMessage()
{
return sprintf('%s #%d created', __CLASS__, $this->getId());
}
public function getRemoveLogMessage()
{
return sprintf('%s #%d removed', __CLASS__, $this->getId());
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* @author Lusitanian
* Freely released with no restrictions, re-license however you'd like!
*/
namespace Knp\DoctrineBehaviors\Model\Sluggable;
/**
* Sluggable trait.
*
* Should be used inside entities for which slugs should automatically be generated on creation for SEO/permalinks.
*/
trait Sluggable
{
use SluggableProperties,
SluggableMethods
;
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* @author Lusitanian
* Freely released with no restrictions, re-license however you'd like!
*/
namespace Knp\DoctrineBehaviors\Model\Sluggable;
/**
* Sluggable trait.
*
* Should be used inside entities for which slugs should automatically be generated on creation for SEO/permalinks.
*/
trait SluggableMethods
{
/**
* Returns an array of the fields used to generate the slug.
*
* @abstract
* @return array
*/
abstract public function getSluggableFields();
/**
* Returns the slug's delimiter
*
* @return string
*/
private function getSlugDelimiter()
{
return '-';
}
/**
* Returns whether or not the slug gets regenerated on update.
*
* @return bool
*/
private function getRegenerateSlugOnUpdate()
{
return true;
}
/**
* Sets the entity's slug.
*
* @param $slug
* @return $this
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Returns the entity's slug.
*
* @return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* @param $values
* @return mixed|string
*/
private function generateSlugValue($values)
{
$usableValues = [];
foreach ($values as $fieldName => $fieldValue) {
if (!empty($fieldValue)) {
$usableValues[] = $fieldValue;
}
}
if (count($usableValues) < 1) {
throw new \UnexpectedValueException(
'Sluggable expects to have at least one usable (non-empty) field from the following: [ ' . implode(array_keys($values), ',') .' ]'
);
}
// generate the slug itself
$sluggableText = implode(' ', $usableValues);
$transliterator = new Transliterator;
$sluggableText = $transliterator->transliterate($sluggableText, $this->getSlugDelimiter());
$urlized = strtolower( trim( preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $sluggableText ), $this->getSlugDelimiter() ) );
$urlized = preg_replace("/[\/_|+ -]+/", $this->getSlugDelimiter(), $urlized);
return $urlized;
}
/**
* Generates and sets the entity's slug. Called prePersist and preUpdate
*/
public function generateSlug()
{
if ( $this->getRegenerateSlugOnUpdate() || empty( $this->slug ) ) {
$fields = $this->getSluggableFields();
$values = [];
foreach ($fields as $field) {
if (property_exists($this, $field)) {
$val = $this->{$field};
} else {
$methodName = 'get' . ucfirst($field);
if (method_exists($this, $methodName)) {
$val = $this->{$methodName}();
} else {
$val = null;
}
}
$values[] = $val;
}
$this->slug = $this->generateSlugValue($values);
}
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* @author Lusitanian
* Freely released with no restrictions, re-license however you'd like!
*/
namespace Knp\DoctrineBehaviors\Model\Sluggable;
/**
* Sluggable trait.
*
* Should be used inside entities for which slugs should automatically be generated on creation for SEO/permalinks.
*/
trait SluggableProperties
{
protected $slug;
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @author Lusitanian
* Freely released with no restrictions, re-license however you'd like!
*/
namespace Knp\DoctrineBehaviors\Model\Sluggable;
/**
* Transliteration utility
*/
class Transliterator extends \Behat\Transliterator\Transliterator
{
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\SoftDeletable;
/**
* SoftDeletable trait.
*
* Should be used inside entity, that needs to be self-deleted.
*/
trait SoftDeletable
{
use SoftDeletableProperties,
SoftDeletableMethods
;
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\SoftDeletable;
/**
* SoftDeletable trait.
*
* Should be used inside entity, that needs to be self-deleted.
*/
trait SoftDeletableMethods
{
/**
* Marks entity as deleted.
*/
public function delete()
{
$this->deletedAt = $this->currentDateTime();
}
/**
* Restore entity by undeleting it
*/
public function restore()
{
$this->deletedAt = null;
}
/**
* Checks whether the entity has been deleted.
*
* @return Boolean
*/
public function isDeleted()
{
if (null !== $this->deletedAt) {
return $this->deletedAt <= $this->currentDateTime();
}
return false;
}
/**
* Checks whether the entity will be deleted.
*
* @return Boolean
*/
public function willBeDeleted(\DateTime $at = null)
{
if ($this->deletedAt === null) {
return false;
}
if ($at === null) {
return true;
}
return $this->deletedAt <= $at;
}
/**
* Returns date on which entity was been deleted.
*
* @return DateTime|null
*/
public function getDeletedAt()
{
return $this->deletedAt;
}
/**
* Set the delete date to given date.
*
* @param DateTime $date
* @param Object
*
* @return $this
*/
public function setDeletedAt(\DateTime $date)
{
$this->deletedAt = $date;
return $this;
}
/**
* Get a instance of \DateTime with the current data time including milliseconds.
*
* @return \DateTime
*/
private function currentDateTime()
{
$dateTime = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)));
$dateTime->setTimezone(new \DateTimeZone(date_default_timezone_get()));
return $dateTime;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\SoftDeletable;
/**
* SoftDeletable trait.
*
* Should be used inside entity, that needs to be self-deleted.
*/
trait SoftDeletableProperties
{
protected $deletedAt;
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Knp\DoctrineBehaviors\Model\Sortable;
trait Sortable
{
/**
* @var int
*/
protected $sort = 1;
/**
* @var bool
*/
private $reordered = false;
/**
* Get sort.
*
* @return int
*/
public function getSort()
{
return $this->sort;
}
/**
* Set sort.
*
* @param int $sort Sort the value to set
*
* @return $this
*/
public function setSort($sort)
{
$this->reordered = $this->sort !== $sort;
$this->sort = $sort;
return $this;
}
/**
* @return bool
*/
public function isReordered()
{
return $this->reordered;
}
/**
* @return $this
*/
public function setReordered()
{
$this->reordered = true;
return $this;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Timestampable;
/**
* Timestampable trait.
*
* Should be used inside entity, that needs to be timestamped.
*/
trait Timestampable
{
use TimestampableProperties,
TimestampableMethods
;
}

View File

@@ -0,0 +1,78 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Timestampable;
/**
* Timestampable trait.
*
* Should be used inside entity, that needs to be timestamped.
*/
trait TimestampableMethods
{
/**
* Returns createdAt value.
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Returns updatedAt value.
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* @param \DateTime $createdAt
* @return $this
*/
public function setCreatedAt(\DateTime $createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* @param \DateTime $updatedAt
* @return $this
*/
public function setUpdatedAt(\DateTime $updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Updates createdAt and updatedAt timestamps.
*/
public function updateTimestamps()
{
// Create a datetime with microseconds
$dateTime = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)));
$dateTime->setTimezone(new \DateTimeZone(date_default_timezone_get()));
if (null === $this->createdAt) {
$this->createdAt = $dateTime;
}
$this->updatedAt = $dateTime;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Timestampable;
/**
* Timestampable trait.
*
* Should be used inside entity, that needs to be timestamped.
*/
trait TimestampableProperties
{
protected $createdAt;
protected $updatedAt;
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
/**
* Translatable trait.
*
* Should be used inside entity, that needs to be translated.
*/
trait Translatable
{
use TranslatableProperties,
TranslatableMethods
;
}

View File

@@ -0,0 +1,227 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Translatable trait.
*
* Should be used inside entity, that needs to be translated.
*/
trait TranslatableMethods
{
/**
* Returns collection of translations.
*
* @return ArrayCollection
*/
public function getTranslations()
{
return $this->translations = $this->translations ?: new ArrayCollection();
}
/**
* Returns collection of new translations.
*
* @return ArrayCollection
*/
public function getNewTranslations()
{
return $this->newTranslations = $this->newTranslations ?: new ArrayCollection();
}
/**
* Adds new translation.
*
* @param Translation $translation The translation
*
* @return $this
*/
public function addTranslation($translation)
{
$this->getTranslations()->set((string)$translation->getLocale(), $translation);
$translation->setTranslatable($this);
return $this;
}
/**
* Removes specific translation.
*
* @param Translation $translation The translation
*/
public function removeTranslation($translation)
{
$this->getTranslations()->removeElement($translation);
}
/**
* Returns translation for specific locale (creates new one if doesn't exists).
* If requested translation doesn't exist, it will first try to fallback default locale
* If any translation doesn't exist, it will be added to newTranslations collection.
* In order to persist new translations, call mergeNewTranslations method, before flush
*
* @param string $locale The locale (en, ru, fr) | null If null, will try with current locale
* @param bool $fallbackToDefault Whether fallback to default locale
*
* @return Translation
*/
public function translate($locale = null, $fallbackToDefault = true)
{
return $this->doTranslate($locale, $fallbackToDefault);
}
/**
* Returns translation for specific locale (creates new one if doesn't exists).
* If requested translation doesn't exist, it will first try to fallback default locale
* If any translation doesn't exist, it will be added to newTranslations collection.
* In order to persist new translations, call mergeNewTranslations method, before flush
*
* @param string $locale The locale (en, ru, fr) | null If null, will try with current locale
* @param bool $fallbackToDefault Whether fallback to default locale
*
* @return Translation
*/
protected function doTranslate($locale = null, $fallbackToDefault = true)
{
if (null === $locale) {
$locale = $this->getCurrentLocale();
}
$translation = $this->findTranslationByLocale($locale);
if ($translation and !$translation->isEmpty()) {
return $translation;
}
if ($fallbackToDefault) {
if (($fallbackLocale = $this->computeFallbackLocale($locale))
&& ($translation = $this->findTranslationByLocale($fallbackLocale))) {
return $translation;
}
if ($defaultTranslation = $this->findTranslationByLocale($this->getDefaultLocale(), false)) {
return $defaultTranslation;
}
}
$class = static::getTranslationEntityClass();
$translation = new $class();
$translation->setLocale($locale);
$this->getNewTranslations()->set((string)$translation->getLocale(), $translation);
$translation->setTranslatable($this);
return $translation;
}
/**
* Merges newly created translations into persisted translations.
*/
public function mergeNewTranslations()
{
foreach ($this->getNewTranslations() as $newTranslation) {
if (!$this->getTranslations()->contains($newTranslation) && !$newTranslation->isEmpty()) {
$this->addTranslation($newTranslation);
$this->getNewTranslations()->removeElement($newTranslation);
}
}
}
/**
* @param mixed $locale the current locale
*/
public function setCurrentLocale($locale)
{
$this->currentLocale = $locale;
}
/**
* @return Returns the current locale
*/
public function getCurrentLocale()
{
return $this->currentLocale ?: $this->getDefaultLocale();
}
/**
* @param mixed $locale the default locale
*/
public function setDefaultLocale($locale)
{
$this->defaultLocale = $locale;
}
/**
* @return Returns the default locale
*/
public function getDefaultLocale()
{
return $this->defaultLocale;
}
/**
* An extra feature allows you to proxy translated fields of a translatable entity.
*
* @param string $method
* @param array $arguments
*
* @return mixed The translated value of the field for current locale
*/
protected function proxyCurrentLocaleTranslation($method, array $arguments = [])
{
return call_user_func_array(
[$this->translate($this->getCurrentLocale()), $method],
$arguments
);
}
/**
* Returns translation entity class name.
*
* @return string
*/
public static function getTranslationEntityClass()
{
return __CLASS__.'Translation';
}
/**
* Finds specific translation in collection by its locale.
*
* @param string $locale The locale (en, ru, fr)
* @param bool $withNewTranslations searched in new translations too
*
* @return Translation|null
*/
protected function findTranslationByLocale($locale, $withNewTranslations = true)
{
$translation = $this->getTranslations()->get($locale);
if ($translation) {
return $translation;
}
if ($withNewTranslations) {
return $this->getNewTranslations()->get($locale);
}
}
protected function computeFallbackLocale($locale)
{
if (strrchr($locale, '_') !== false) {
return substr($locale, 0, -strlen(strrchr($locale, '_')));
}
return false;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
/**
* Translatable trait.
*
* Should be used inside entity, that needs to be translated.
*/
trait TranslatableProperties
{
/**
* Will be mapped to translatable entity
* by TranslatableSubscriber
*/
protected $translations;
/**
* Will be merged with persisted translations on mergeNewTranslations call
*
* @see mergeNewTranslations
*/
protected $newTranslations;
/**
* currentLocale is a non persisted field configured during postLoad event
*/
protected $currentLocale;
protected $defaultLocale = 'en';
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
/**
* Translation trait.
*
* Should be used inside translation entity.
*/
trait Translation
{
use TranslationProperties,
TranslationMethods
;
}

View File

@@ -0,0 +1,108 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
/**
* Translation trait.
*
* Should be used inside translation entity.
*/
trait TranslationMethods
{
/**
* Returns the translatable entity class name.
*
* @return string
*/
public static function getTranslatableEntityClass()
{
// By default, the translatable class has the same name but without the "Translation" suffix
return substr(__CLASS__, 0, -11);
}
/**
* Returns object id.
*
* @return mixed
*/
public function getId() {
return $this->id;
}
/**
* Sets entity, that this translation should be mapped to.
*
* @param Translatable $translatable The translatable
*
* @return $this
*/
public function setTranslatable($translatable)
{
$this->translatable = $translatable;
return $this;
}
/**
* Returns entity, that this translation is mapped to.
*
* @return Translatable
*/
public function getTranslatable()
{
return $this->translatable;
}
/**
* Sets locale name for this translation.
*
* @param string $locale The locale
*
* @return $this
*/
public function setLocale($locale)
{
$this->locale = $locale;
return $this;
}
/**
* Returns this translation locale.
*
* @return string
*/
public function getLocale()
{
return $this->locale;
}
/**
* Tells if translation is empty
*
* @return bool true if translation is not filled
*/
public function isEmpty()
{
foreach (get_object_vars($this) as $var => $value) {
if (in_array($var, ['id', 'translatable', 'locale'])) {
continue;
}
if (!empty($value)) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Translatable;
/**
* Translation trait.
*
* Should be used inside translation entity.
*/
trait TranslationProperties
{
/**
* @var int
*/
protected $id;
/**
* @var string
*/
protected $locale;
/**
* Will be mapped to translatable entity
* by TranslatableSubscriber
*/
protected $translatable;
}

View File

@@ -0,0 +1,333 @@
<?php
/**
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Knp\DoctrineBehaviors\Model\Tree;
use Knp\DoctrineBehaviors\Model\Tree\NodeInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/*
* @author Florian Klein <florian.klein@free.fr>
*/
trait Node
{
/**
* @var ArrayCollection $childNodes the children in the tree
*/
private $childNodes;
/**
* @var NodeInterface $parentNode the parent in the tree
*/
private $parentNode;
protected $materializedPath = '';
public function getNodeId()
{
return $this->getId();
}
/**
* Returns path separator for entity's materialized path.
*
* @return string "/" by default
*/
public static function getMaterializedPathSeparator()
{
return '/';
}
/**
* {@inheritdoc}
**/
public function getRealMaterializedPath()
{
return $this->getMaterializedPath() . self::getMaterializedPathSeparator() . $this->getNodeId();
}
public function getMaterializedPath()
{
return $this->materializedPath;
}
/**
* {@inheritdoc}
**/
public function setMaterializedPath($path)
{
$this->materializedPath = $path;
$this->setParentMaterializedPath($this->getParentMaterializedPath());
return $this;
}
/**
* {@inheritdoc}
**/
public function getParentMaterializedPath()
{
$path = $this->getExplodedPath();
array_pop($path);
$parentPath = static::getMaterializedPathSeparator().implode(static::getMaterializedPathSeparator(), $path);
return $parentPath;
}
/**
* {@inheritdoc}
**/
public function setParentMaterializedPath($path)
{
$this->parentNodePath = $path;
}
/**
* {@inheritdoc}
**/
public function getRootMaterializedPath()
{
$explodedPath = $this->getExplodedPath();
return static::getMaterializedPathSeparator() . array_shift($explodedPath);
}
/**
* {@inheritdoc}
**/
public function getNodeLevel()
{
return count($this->getExplodedPath());
}
public function isRootNode()
{
return self::getMaterializedPathSeparator() === $this->getParentMaterializedPath();
}
public function isLeafNode()
{
return 0 === $this->getChildNodes()->count();
}
/**
* {@inheritdoc}
**/
public function getChildNodes()
{
return $this->childNodes = $this->childNodes ?: new ArrayCollection;
}
/**
* {@inheritdoc}
**/
public function addChildNode(NodeInterface $node)
{
$this->getChildNodes()->add($node);
}
/**
* {@inheritdoc}
**/
public function isIndirectChildNodeOf(NodeInterface $node)
{
return $this->getRealMaterializedPath() !== $node->getRealMaterializedPath()
&& 0 === strpos($this->getRealMaterializedPath(), $node->getRealMaterializedPath());
}
/**
* {@inheritdoc}
**/
public function isChildNodeOf(NodeInterface $node)
{
return $this->getParentMaterializedPath() === $node->getRealMaterializedPath();
}
/**
* {@inheritdoc}
**/
public function setChildNodeOf(NodeInterface $node = null)
{
$id = $this->getNodeId();
if (empty($id)) {
throw new \LogicException('You must provide an id for this node if you want it to be part of a tree.');
}
$path = null !== $node
? rtrim($node->getRealMaterializedPath(), static::getMaterializedPathSeparator())
: static::getMaterializedPathSeparator()
;
$this->setMaterializedPath($path);
if (null !== $this->parentNode) {
$this->parentNode->getChildNodes()->removeElement($this);
}
$this->parentNode = $node;
if (null !== $node) {
$this->parentNode->addChildNode($this);
}
foreach ($this->getChildNodes() as $child) {
$child->setChildNodeOf($this);
}
return $this;
}
/**
* {@inheritdoc}
**/
public function getParentNode()
{
return $this->parentNode;
}
/**
* {@inheritdoc}
**/
public function setParentNode(NodeInterface $node)
{
$this->parentNode = $node;
$this->setChildNodeOf($this->parentNode);
return $this;
}
/**
* {@inheritdoc}
**/
public function getRootNode()
{
$parent = $this;
while (null !== $parent->getParentNode()) {
$parent = $parent->getParentNode();
}
return $parent;
}
/**
* {@inheritdoc}
**/
public function buildTree(array $results)
{
$this->getChildNodes()->clear();
foreach ($results as $i => $node) {
if ($node->getMaterializedPath() === $this->getRealMaterializedPath()) {
$node->setParentNode($this);
$node->buildTree($results);
}
}
}
/**
* @param \Closure $prepare a function to prepare the node before putting into the result
*
* @return string the json representation of the hierarchical result
**/
public function toJson(\Closure $prepare = null)
{
$tree = $this->toArray($prepare);
return json_encode($tree);
}
/**
* @param \Closure $prepare a function to prepare the node before putting into the result
* @param array $tree a reference to an array, used internally for recursion
*
* @return array the hierarchical result
**/
public function toArray(\Closure $prepare = null, array &$tree = null)
{
if (null === $prepare) {
$prepare = function(NodeInterface $node) {
return (string) $node;
};
}
if (null === $tree) {
$tree = array($this->getNodeId() => array('node' => $prepare($this), 'children' => array()));
}
foreach ($this->getChildNodes() as $node) {
$tree[$this->getNodeId()]['children'][$node->getNodeId()] = array('node' => $prepare($node), 'children' => array());
$node->toArray($prepare, $tree[$this->getNodeId()]['children']);
}
return $tree;
}
/**
* @param \Closure $prepare a function to prepare the node before putting into the result
* @param array $tree a reference to an array, used internally for recursion
*
* @return array the flatten result
**/
public function toFlatArray(\Closure $prepare = null, array &$tree = null)
{
if (null === $prepare) {
$prepare = function(NodeInterface $node) {
$pre = $node->getNodeLevel() > 1 ? implode('', array_fill(0, $node->getNodeLevel(), '--')) : '';
return $pre.(string) $node;
};
}
if (null === $tree) {
$tree = array($this->getNodeId() => $prepare($this));
}
foreach ($this->getChildNodes() as $node) {
$tree[$node->getNodeId()] = $prepare($node);
$node->toFlatArray($prepare, $tree);
}
return $tree;
}
public function offsetSet($offset, $node)
{
$node->setChildNodeOf($this);
return $this;
}
public function offsetExists($offset)
{
return isset($this->getChildNodes()[$offset]);
}
public function offsetUnset($offset)
{
unset($this->getChildNodes()[$offset]);
}
public function offsetGet($offset)
{
return $this->getChildNodes()[$offset];
}
/**
* {@inheritdoc}
**/
protected function getExplodedPath()
{
$path = explode(static::getMaterializedPathSeparator(), $this->getRealMaterializedPath());
return array_filter($path, function($item) {
return '' !== $item;
});
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Knp\DoctrineBehaviors\Model\Tree;
use Doctrine\Common\Collections\Collection;
/**
* Tree\Node defines a set of needed methods
* to work with materialized path tree nodes
*
* @author Florian Klein <florian.klein@free.fr>
*/
interface NodeInterface
{
/**
* @return string the field that will represent the node in the path
**/
public function getNodeId();
/**
* @return string the materialized path,
* eg the representation of path from all ancestors
**/
public function getMaterializedPath();
/**
* @return string the real materialized path,
* eg the representation of path from all ancestors + current node
**/
public function getRealMaterializedPath();
/**
* @return string the materialized path from the parent, eg: the representation of path from all parent ancestors
**/
public function getParentMaterializedPath();
/**
* Set parent path.
*
* @param string $path the value to set.
*/
public function setParentMaterializedPath($path);
/**
* @return NodeInterface the parent node
**/
public function getParentNode();
/**
* @param string $path the materialized path, eg: the the materialized path to its parent
*
* @return NodeInterface $this Fluent interface
**/
public function setMaterializedPath($path);
/**
* Used to build the hierarchical tree.
* This method will do:
* - modify the parent of this node
* - Add the this node to the children of the new parent
* - Remove the this node from the children of the old parent
* - Modify the materialized path of this node and all its children, recursively
*
* @param NodeInterface | null $node The node to use as a parent
*
* @return NodeInterface $this Fluent interface
**/
public function setChildNodeOf(NodeInterface $node = null);
/**
* @param NodeInterface $node the node to append to the children collection
*
* @return NodeInterface $this Fluent interface
**/
public function addChildNode(NodeInterface $node);
/**
* @return Collection the children collection
**/
public function getChildNodes();
/**
* @return bool if the node is a leaf (i.e has no children)
**/
public function isLeafNode();
/**
* @return bool if the node is a root (i.e has no parent)
**/
public function isRootNode();
/**
* @return NodeInterface
**/
public function getRootNode();
/**
* Tells if this node is a child of another node
* @param NodeInterface $node the node to compare with
*
* @return boolean true if this node is a direct child of $node
**/
public function isChildNodeOf(NodeInterface $node);
/**
*
* @return integer the level of this node, eg: the depth compared to root node
**/
public function getNodeLevel();
/**
* Builds a hierarchical tree from a flat collection of NodeInterface elements
*
* @return void
**/
public function buildTree(array $nodes);
}