netbull / doctrine-behaviors
Doctrine2行为特性
Requires
- php: ~7.0
- behat/transliterator: ~1.0
- doctrine/common: >=2.2
Requires (Dev)
- ext-pdo_mysql: *
- ext-pdo_pgsql: *
- ext-pdo_sqlite: *
- doctrine/orm: >=2.4.5
- hexmedia/yaml-linter: ~0.1
- jakub-onderka/php-parallel-lint: ~0.8
- phpunit/phpunit: ~4.8
Suggests
- symfony/framework-bundle: To be able to use it as a bundle
This package is auto-updated.
Last update: 2024-09-20 17:58:43 UTC
README
Doctrine2 Behaviors
- 分支
master
包含下一个 v2.0 版本 - 分支
v1
包含当前 v1.x 版本
这是一个PHP >=7.0
库,包含一系列特性接口,用于向Doctrine2实体和仓库添加行为。
目前它处理以下行为:
- blameable(责任归属)
- filterable(可过滤)
- geocodable(可地理编码)
- joinable(可连接)
- loggable(可记录日志)
- sluggable(可生成别名)
- softDeletable(软删除)
- sortable(可排序)
- timestampable(可时间戳)
- translatable(可翻译)
- tree(树状结构)
此项目正在寻找维护者
我们意识到我们再也没有那么多时间来维护这个项目了,因此我们正在寻找维护者。如果你希望继续工作,请创建一个问题。
注意
一些行为(可翻译、可时间戳、软删除、责任归属、可地理编码)需要Doctrine订阅者才能工作。请确保通过阅读订阅者部分来激活它们。
安装
composer require knplabs/doctrine-behaviors:~1.1
配置
默认情况下,当与Symfony集成时,所有订阅者都是启用的(如果你没有为该包指定任何配置)。但是,你可以通过白名单方式启用所需的行为
knp_doctrine_behaviors: blameable: false geocodable: ~ # Here null is converted to false loggable: ~ sluggable: true soft_deletable: true # All others behaviors are disabled
订阅者
如果你使用symfony2,你可以轻松地在以下位置注册它们:
- 推荐方法
添加到AppKernel
class AppKernel { function registerBundles() { $bundles = array( //... new Knp\DoctrineBehaviors\Bundle\DoctrineBehaviorsBundle(), //... ); //... return $bundles; } }
- 已弃用方法:导入服务定义文件
# app/config/config.yml imports: - { resource: ../../vendor/knplabs/doctrine-behaviors/config/orm-services.yml }
你也可以使用doctrine2 api来注册它们
<?php $em->getEventManager()->addEventSubscriber(new \Knp\DoctrineBehaviors\ORM\Translatable\TranslatableSubscriber); // register more if needed
使用方法
你所要做的就是定义一个Doctrine2实体并使用特性
<?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\Model as ORMBehaviors; /** * @ORM\Entity(repositoryClass="CategoryRepository") */ class Category implements ORMBehaviors\Tree\NodeInterface, \ArrayAccess { use ORMBehaviors\Blameable\Blameable, ORMBehaviors\Geocodable\Geocodable, ORMBehaviors\Loggable\Loggable, ORMBehaviors\Sluggable\Sluggable, ORMBehaviors\SoftDeletable\SoftDeletable, ORMBehaviors\Sortable\Sortable, ORMBehaviors\Timestampable\Timestampable, ORMBehaviors\Translatable\Translatable, ORMBehaviors\Tree\Node ; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="NONE") */ protected $id; }
对于一些像树这样的行为,你可以使用仓库特性
<?php use Doctrine\ORM\EntityRepository; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; class CategoryRepository extends EntityRepository { use ORMBehaviors\Tree\Tree, }
就是这样!
现在你有一个可以工作的Category
,它的行为就像:
tree(树状结构)
<?php $category = new Category; $category->setId(1); // tree nodes need an id to construct path. $child = new Category; $child->setId(2); $child->setChildNodeOf($category); $em->persist($child); $em->persist($category); $em->flush(); $root = $em->getRepository('Category')->getTree(); $root->getParentNode(); // null $root->getChildNodes(); // ArrayCollection $root[0][1]; // node or null $root->isLeafNode(); // boolean $root->isRootNode(); // boolean
你可以使用除
id
以外的另一个标识符,只需覆盖getNodeId
并返回你的自定义标识符(与Sluggable
结合使用效果很好)
translatable(可翻译)
如果你正在处理一个Category
实体,默认情况下Translatable
行为期望在Category实体同一文件夹中有一个CategoryTranslation
实体。
默认命名约定(或通过特性方法进行自定义)可以避免你手动处理实体关联。它将由TranslationSubscriber自动处理。
为了使用可翻译特性,你必须创建这个CategoryTranslation
实体。
<?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\Model as ORMBehaviors; /** * @ORM\Entity */ class CategoryTranslation { use ORMBehaviors\Translatable\Translation; /** * @ORM\Column(type="string", length=255) */ protected $name; /** * @ORM\Column(type="string", length=255) */ protected $description; /** * @return string */ public function getName() { return $this->name; } /** * @param string * @return null */ public function setName($name) { $this->name = $name; } /** * @return string */ public function getDescription() { return $this->description; } /** * @param string * @return null */ public function setDescription($description) { $this->description = $description; } }
相应的Category实体需要use ORMBehaviors\Translatable\Translatable;
并且应该只包含不需要翻译的字段。
<?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\Model as ORMBehaviors; /** * @ORM\Entity */ class Category { use ORMBehaviors\Translatable\Translatable; /** * @ORM\Column(type="string", length=255) */ protected $someFieldYouDoNotNeedToTranslate; }
更新数据库后,例如使用./console doctrine:schema:update --force
,你现在可以使用translate
或getTranslations
方法进行翻译工作。
<?php $category = new Category; $category->translate('fr')->setName('Chaussures'); $category->translate('en')->setName('Shoes'); $em->persist($category); // In order to persist new translations, call mergeNewTranslations method, before flush $category->mergeNewTranslations(); $category->translate('en')->getName();
覆盖
如果你更喜欢为翻译实体使用不同的类名,或者想使用单独的命名空间,你有两种方法
如果你想在全局范围内定义一个自定义翻译实体类名:覆盖特性Translatable
及其方法getTranslationEntityClass
和特性Translation
及其方法getTranslatableEntityClass
在翻译实体中。如果你覆盖了一个,你也需要覆盖另一个以返回相反的类。
示例:假设你想创建一个子命名空间AppBundle\Entity\Translation来存储翻译类,然后在那个文件夹中放置覆盖的特性。
<?php namespace AppBundle\Entity\Translation; use Knp\DoctrineBehaviors\Model\Translatable\Translatable; use Symfony\Component\PropertyAccess\PropertyAccess; trait TranslatableTrait { use Translatable; /** * @inheritdoc */ public static function getTranslationEntityClass() { $explodedNamespace = explode('\\', __CLASS__); $entityClass = array_pop($explodedNamespace); return '\\'.implode('\\', $explodedNamespace).'\\Translation\\'.$entityClass.'Translation'; } }
<?php namespace AppBundle\Entity\Translation; use Knp\DoctrineBehaviors\Model\Translatable\Translation; trait TranslationTrait { use Translation; /** * @inheritdoc */ public static function getTranslatableEntityClass() { $explodedNamespace = explode('\\', __CLASS__); $entityClass = array_pop($explodedNamespace); // Remove Translation namespace array_pop($explodedNamespace); return '\\'.implode('\\', $explodedNamespace).'\\'.substr($entityClass, 0, -11); } }
如果你使用这种方法,请确保覆盖DoctrineBehaviors的特性参数
parameters: knp.doctrine_behaviors.translatable_subscriber.translatable_trait: AppBundle\Entity\Translation\TranslatableTrait knp.doctrine_behaviors.translatable_subscriber.translation_trait: AppBundle\Entity\Translation\TranslationTrait
如果您只想为单个可翻译类定义自定义翻译实体类名:请覆盖可翻译实体中的 getTranslationEntityClass
特性方法和翻译实体中的 getTranslatableEntityClass
方法。如果您覆盖了一个,也需要覆盖另一个以返回逆类。
猜测当前区域设置
您可以通过将其第一个参数设置为可调用对象来配置订阅者猜测当前区域设置的方式。此库提供了一个可调用对象(Knp\DoctrineBehaviors\ORM\Translatable\CurrentLocaleCallable
),它使用 Symfony2 返回当前区域设置。
代理翻译
一个额外功能允许您代理可翻译实体的翻译字段。
您可以在您可翻译实体的 __call
魔法方法中使用它,这样当您尝试调用 getName
(例如)时,它将返回当前区域设置的名称的翻译值。
<?php public function __call($method, $arguments) { return $this->proxyCurrentLocaleTranslation($method, $arguments); } // or do it with PropertyAccessor that ships with Symfony SE // if your methods don't take any required arguments public function __call($method, $arguments) { return \Symfony\Component\PropertyAccess\PropertyAccess::createPropertyAccessor()->getValue($this->translate(), $method); }
软删除
<?php $category = new Category; $em->persist($category); $em->flush(); // get id $id = $category->getId(); // now remove it $em->remove($category); $em->flush(); // hey, I'm still here: $category = $em->getRepository('Category')->findOneById($id); // but I'm "deleted" $category->isDeleted(); // === true // restore me $category->restore(); //look ma, I am back $category->isDeleted(); // === false //do not forget to call flush method to apply the change $em->flush();
<?php $category = new Category; $em->persist($category); $em->flush(); // I'll delete you tomorrow $category->setDeletedAt((new \DateTime())->modify('+1 day')); // OK, I'm here $category->isDeleted(); // === false /* * 24 hours later... */ // OK, I'm deleted $category->isDeleted(); // === true
timestampable(可时间戳)
<?php $category = new Category; $em->persist($category); $em->flush(); $id = $category->getId(); $category = $em->getRepository('Category')->findOneById($id); $category->getCreatedAt(); $category->getUpdatedAt();
如果您希望更改用于时间戳模型的数据库字段的数据类型,可以设置以下参数,如下所示:
parameters: knp.doctrine_behaviors.timestampable_subscriber.db_field_type: datetimetz
datetimetz
如果您正在使用 PostgreSQL 数据库,这是一个很有用的选项,否则您可能会遇到一些时区问题。有关更多信息,请参阅:[http://doctrine-dbal.readthedocs.org/en/latest/reference/known-vendor-issues.html#datetime-datetimetz-and-time-types](http://doctrine-dbal.readthedocs.org/en/latest/reference/known-vendor-issues.html#datetime-datetimetz-and-time-types)
默认类型是 datetime
。
blameable(责任归属)
可追究责任的功能能够追踪给定实体的创建者和更新者。一个可追究责任的 可调用对象 用于从您的应用程序中获取当前用户。
如果您使用 Doctrine 实体来表示您的用户,您可以配置订阅者来自动管理此用户实体与您的实体之间的关联。
使用 symfony2,您只需要配置名为 %knp.doctrine_behaviors.blameable_subscriber.user_entity%
的 DI 参数,例如
# app/config/config.yml
parameters:
knp.doctrine_behaviors.blameable_subscriber.user_entity: AppBundle\Entity\User
然后,您可以使用它,如下所示
<?php $category = new Category; $em->persist($category); // instances of %knp.doctrine_behaviors.blameable_subscriber.user_entity% $creator = $category->getCreatedBy(); $updater = $category->getUpdatedBy();
loggable(可记录日志)
可记录的功能能够追踪生命周期修改并使用任何第三方日志系统记录它们。一个可记录的 可调用对象 用于从任何您想要的地方获取记录器。
<?php /** * @ORM\Entity */ class Category { use ORMBehaviors\Loggable\Loggable; // you can override the default log messages defined in trait: public function getUpdateLogMessage(array $changeSets = []) { return 'Changed: '.print_r($changeSets, true); } public function getRemoveLogMessage() { return 'removed!'; } }
然后,将这些消息传递到配置的可调用对象。您可以通过向 LoggableSubscriber 传递另一个可调用对象来定义自己的。
<?php $em->getEventManager()->addEventSubscriber( new \Knp\DoctrineBehaviors\ORM\Loggable\LoggableSubscriber( new ClassAnalyzer, function($message) { // do stuff with message } ) );
如果您使用 symfony,您也可以配置要使用哪个可调用对象
// app/config/config.yml
parameters:
knp.doctrine_behaviors.loggable_subscriber.logger_callable.class: Your\InvokableClass
geocodable(可地理编码)
可地理编码的功能为 PostgreSQL 平台提供了扩展,以便使用 cube 和 earthdistance 扩展。
它允许您基于地理坐标查询实体。它还提供了一个简单的方法来使用第三方库,如出色的 geocoder 将地址转换为纬度和经度。
<?php $geocoder = new \Geocoder\Geocoder; // register geocoder providers // $subscriber instanceof GeocodableSubscriber (add "knp.doctrine_behaviors.geocodable_subscriber" into your services.yml) $subscriber->setGeolocationCallable(function($entity) use($geocoder) { $location = $geocoder->geocode($entity->getAddress()); return new Point( $location->getLatitude(), $location->getLongitude() )); }); $category = new Category; $em->persist($category); $location = $category->getLocation(); // instanceof Point // find cities in a circle of 500 km around point 47 lon., 7 lat. $nearCities = $repository->findByDistance(new Point(47, 7), 500);
sluggable(可生成别名)
可缩略功能为实体生成缩略名(唯一性不可保证)。将在更新/持久化时自动生成(您可以通过覆盖 getRegenerateSlugOnUpdate
并返回 false 来禁用更新时的生成。您还可以通过覆盖 getSlugDelimiter
来覆盖默认的连字符作为缩略名分隔符。您可以通过覆盖 generateSlugValue
来更改缩略名生成算法。用例包括 SEO(例如,像 http://example.com/post/3/introduction-to-php 这样的 URL)
<?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\Model as ORMBehaviors; /** * @ORM\Entity */ class BlogPost { use ORMBehaviors\Sluggable\Sluggable; /** * @ORM\Column(type="string") */ protected $title; public function getSluggableFields() { return [ 'title' ]; } public function generateSlugValue($values) { return implode('-', $values); } }
filterable(可过滤)
可筛选的可以在存储库级别使用
它允许我们简单地筛选结果
联合筛选示例
<?php use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="ProductRepository") */ class ProductEntity { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(type="string", nullable=true) */ private $name; /** * @ORM\Column(type="integer") */ private $code; /** * @ORM\OneToMany(targetEntity="Order", mappedBy="product") */ protected $orders; }
和存储库
<?php use Knp\DoctrineBehaviors\ORM\Filterable; use Doctrine\ORM\EntityRepository; class ProductRepository extends EntityRepository { use Filterable\FilterableRepository; public function getLikeFilterColumns() { return ['e:name', 'o:code']; } public function getEqualFilterColumns() { return []; } protected function createFilterQueryBuilder() { return $this ->createQueryBuilder('e') ->leftJoin('e.orders', 'o'); } }
现在我们可以使用以下方法进行筛选
$products = $em->getRepository('Product')->filterBy(['o:code' => '21']);
可调用对象
可调用对象被一些订阅者(如blameable和geocodable)使用,以基于第三方系统填充信息。
例如,blameable可调用对象可以是实现__invoke
方法的任何symfony2服务或任何匿名函数,只要它们返回当前登录用户表示(这意味着一切,一个用户实体,一个字符串,用户名等)。关于被调用的DI服务的示例,请查看Knp\DoctrineBehaviors\ORM\Blameable\UserCallable
类。
对于geocodable,您可以将它设置为任何实现__invoke
的服务或返回Knp\DoctrineBehaviors\ORM\Geocodable\Type\Point
对象的匿名函数。
测试
维护者
KNPLabs正在寻找维护者(查看原因)。
如果您感兴趣,请随意提交一个PR请求以请求被添加为维护者。
我们很高兴听到您的消息 :)