Начиная с PHP 5.4.0, PHP поддерживает красивый способ повторного использования кода, называемого «черты» — набор методов, которые вы можете включить в другой класс, чтобы не повторяться. Вы можете прочитать больше о чертах в ранее опубликованных сообщениях SitePoint: здесь , здесь и здесь .
Сегодня я собираюсь показать вам, как их можно использовать с Doctrine ORM в среде Symfony.
Основы черт
<?php
trait ExampleTrait {
public function sayHello() {
echo "Hello";
}
}
class A {
use ExampleTrait;
}
class B {
use ExampleTrait;
}
$one = new A();
$one->sayHello(); /* return `Hello` */
$two = new B();
$two->sayHello(); /* return `Hello`, too */
Как мы видим, базовый метод sayHello()
A
B
use
Article
Легко, правда? Этот пример очень короткий, но он должен дать вам базовые знания для работы с чертами.
Если вас интересуют черты, я рекомендую вам прочитать официальную документацию и ранее опубликованные сообщения SitePoint здесь , здесь и здесь , чтобы полностью понять концепцию.
Позвольте мне предупредить вас о том, что многие люди не видят разницы между чертами и интерфейсами . Вот прагматичное объяснение:
Интерфейс — это контракт, в котором говорится, что «этот объект способен делать эту вещь», тогда как Черта дает объекту возможность делать эту вещь.
Для более глубокого объяснения, не стесняйтесь взглянуть на этот проницательный пост Филиппа Брауна, откуда взялась предыдущая цитата.
Когда дело доходит до организации архитектуры базы данных, нередко приходится сталкиваться с дублированием кода. В качестве примера, предположим, что мы должны разработать обычное приложение для блога. В какой-то момент вполне вероятно, что мы собираемся создать базовую сущность Comment
created_at
Обе сущности выиграют от наличия полей updated_at
<?php
namespace Blog\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="article")
* @ORM\Entity(repositoryClass="Blog\AppBundle\Entity\ArticleRepository")
*/
class Article
{
/**
* @ORM\Column(name="idArticle" type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/* Other properties you need in your entity: $title, $content, $author... */
/** @ORM\Column(name="created_at" type="datetime") */
private $createdAt;
/** @ORM\Column(name="updated_at" type="datetime") */
private $updatedAt;
/* Getters & Setters */
} Но прежде чем копаться в чертах, давайте посмотрим, как мы можем построить эти сущности в Доктрине без них.
Шаг 1: создайте сущности
Статья сущность
<?php
namespace Blog\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="comment")
* @ORM\Entity(repositoryClass="Blog\AppBundle\Entity\CommentRepository")
*/
class Comment
{
/**
* @ORM\Column(name="idComment" type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/* Other properties you need in your entity */
/** @ORM\Column(name="created_at" type="datetime") */
private $createdAt;
/** @ORM\Column(name="updated_at" type="datetime") */
private $updatedAt;
/* Getters & Setters */
}
Комментарий сущность
$createdAt
В оба класса включены одинаковые свойства $updatedAt
<?php
// src/Blog/AppBundle/Entity/Traits/TimestampableTrait.php
namespace Blog\AppBundle\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait TimestampableTrait
{
/**
* @var datetime $createdAt
*
* @ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* @var datetime $updatedAt
*
* @ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
/**
* Get createdAt
*
* @return datetime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set createdAt
*
* @param datetime $createdAt
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get updatedAt
*
* @return datetime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set updatedAt
*
* @param datetime $updatedAt
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
} Это далеко от СУХОГО. Сможет ли Traits помочь нам очистить этот код?
Шаг 2: создать черту
$createdAt
Вот довольно симпатичный файл, в который мы переместили исходный дублированный код. И $updatedAt
use
В результате их будет гораздо проще использовать где-то еще. Помните раздел введения с использованием ключевого слова.
Шаг 3: рефакторинг сущностей
Статья сущность
<?php
// src/Blog/AppBundle/Entity/Article.php
namespace Blog\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Blog\AppBundle\Entity\Traits\TimestampableTrait;
class Article
{
use TimestampableTrait;
/**
* @ORM\Column(name="idArticle" type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/* Other properties you need in your entity */
/* Getters & Setters */
}
Комментарий сущность
<?php
// src/Blog/AppBundle/Entity/Comment.php
namespace Blog\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Blog\AppBundle\Entity\Traits\TimestampableTrait;
/**
* @ORM\Table(name="comment")
* @ORM\Entity(repositoryClass="Blog\AppBundle\Entity\CommentRepository")
*/
class Comment
{
use TimestampableTrait;
/**
* @ORM\Column(name="idComment" type="integer")
* @ORM\Id()
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/* Other properties you need in your entity */
/* Getters & Setters */
}
Выполнено! Давайте поиграем с командной строкой. Сначала давайте создадим сущности в нашей базе данных:
php app/console doctrine:schema:create
Эта команда выдаст:
`Article Entity`
| idArticle | *All our other fields...* | created_at | updated_at |
|-----------|---------------------------|------------|------------|
`Comment Entity`
| idComment | *All our other fields...* | created_at | updated_at |
|-----------|---------------------------|------------|------------|
Теперь, если вы хотите создать новые объекты из этих классов, вы обнаружите, что у них обоих есть общие доступные методы:
<?php
$article = new Article();
$article->setUpdatedAt(new \Datetime('now'));
$comment = new Comment();
$comment->setUpdatedAt(new \Datetime('now'));
?>
Очевидно, что теперь мы готовы сохранить данные.
Идти дальше
В настоящее время в сфере Symfony многие пакеты и расширения, как правило, придерживаются этого способа работы. Библиотека DoctrineBehaviors от KNPLabs предоставляет большую коллекцию черт для сущностей и репозиториев. В том же состоянии, я рекомендую вам подробно взглянуть на хорошо известный пакет DoctrineExtensions и особенно все, что касается расширения поведения Timestampable .
Последние мысли
Черты не трудно усвоить. Они являются отличным способом создания более легкого и гибкого кода. Будьте осторожны, чтобы не злоупотреблять ими: иногда лучше создать реализацию уникального класса. Я не могу не подчеркнуть, насколько важно занимать достаточно времени, чтобы правильно разработать приложение. Дайте им шанс, если вы думаете, что они могут помочь вам. Создайте свой, протестируйте их и расскажите нам, как вы их использовали!