ãã®è¨äºã¯ãSymfony Advent Calendar 2014 - Qiitaã®7æ¥ç®ã®è¨äºã§ãã
Symfony2ã§éçºãã¦ããã¨å¤§æµã®äººããä¸è©±ã«ãªã£ã¦ãããStofDoctrineExtensionsBundleã§ãããTimestampable(使æ¥ãæ´æ°æ¥ãèªåæ¿å ¥ã§ãã)以å¤ã«ã便å©ãªæ©è½ãæä¾ããã¦ããã®ã§ãã¤ãã£ã¦ã¿ã¾ããã
StofDoctrineExtensionsBundle ã¨ã¯
ç°¡åã«è¨ãã¨ãO/Rãããã¼ã§ããDocrtineãæ¡å¼µããDoctrineExtensionsãç°¡åã«ä½¿ããããã«ãã³ãã«åããã®ããStofDoctrineExtensionsBundleã§ãã
使æ¥ãæ´æ°æ¥ãèªåã§æ¿å ¥ãã¦ãããTimestampableã使ã£ã¦ãã人ãå¤ããã¨æãã¾ãããããã¥ã¡ã³ããè¦ãã¨è²ã ãªæ©è½ãæä¾ãã¦ãããã¨ããããã¾ãã
DoctrineExtensions's featuresãè¦ãã¨ã次ã®ãããªæ©è½ãããã¾ãã
ä¸é¨(ã¨ãããã»ã¨ãã©)ããããã£ã¦ããªãæ©è½ãããã¾ãã
Tree
- æ¨æ§é ã®ãããªæ±ãããã¦ããããã«ãã´ãªãªã©ã«åãã¦ãã
Translatable
- ãã±ã¼ã«(Locale)ãè¨å®ãããã¨ã§ä»è¨èªã§æ¿å ¥ãããã¼ã¿ãåå¾ã§ãã
Sluggable
- ã¹ã©ãã°å(?)ããæååãåå¾ã§ãã
- e.g. "the title", "my code" => "the-title-my-code"
Timestampable
- 使æ¥(CreatedAt)ãæ´æ°æ¥(UpdatedAt)ãèªåã§æ¿å ¥ã»å¤æ´ãã¦ããã
- (ãããã)使ç¨é »åº¦No.1
Blameable
- ã¨ã³ãã£ãã£ãæ°è¦ä½æãæ´æ°ããã¨å¥ã®ã¨ã³ãã£ãã£ã®è¦ç´ ãæ´æ°ãã¦ããã(?)
- Timestampableã¨ä¼¼ã¦ããããæååããªãã¸ã§ã¯ããªã©ãæ¿å ¥ãã¦ããã
Loggable
- ã¨ã³ãã£ãã£ã«å¤æ´å±¥æ´ãæ®ããã¨ãã§ããæã®ãã¼ã¸ã§ã³ã«æ»ããã¨ãã§ãã
Sortable
- ã¨ã³ãã£ãã£ã®ã½ã¼ããã¼ãæ´æ°ãã¦ããã
- Todoãªã¹ãã¿ããã«é çª(position)ã管çãããå ´åã«ä¾¿å©
Translator
- Translatable ã¨ã©ãéãã®ããããããããªã
Softdeleteable
$em->remove()
ããã¨ãã«ãå餿¥(deletedAt)ã«æ¥ä»ãå ¥ãããã¨ã§ã½ããããªã¼ããã¦ããã
Uploadable
- ãã¡ã¤ã«ã®ã¢ãããã¼ãããªãã¼ã ãç§»åãªã©ããµãã¼ããã¦ããã
Reference Integrity
- MongoDBç¨ã«ã¨ã³ãã£ãã£éã§ãã¼ã¿ã®æ´åæ§ãä¿ã¤ã®ããµãã¼ããã¦ããã
å®éã«ä½¿ã£ã¦ã¿ã
Timestampable, Softdeleteable, Sortableã使ã£ã¦ã¿ãã®ã§ä½¿ãæ¹ããã£ã¨è¦ã¦ããã¾ãã
ãªãã¸ããªã¯ãã¡ã â ohsawa0515/simulist (develop branch)
ERå³
ãããªæãã§ãããããã·ã³ãã«ã§ãã
ã«ã©ã åããã¼ãã«åãè¤æ°å½¢ã«ãªã£ãããªããªãã£ãããã¦ãã¾ããæ°ã«ããªãã§ãã ããã
- Projectãã¼ã㫠・・・ Todoãªã¹ãã管çãã
- Listsãã¼ã㫠・・・ ã¿ã¹ã¯
ä¸å¿ãSQLãè¼ãã¦ããã¾ãã
CREATE TABLE IF NOT EXISTS `project` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(300) NOT NULL COMMENT 'ããã¸ã§ã¯ãå',
`identify` VARCHAR(100) NOT NULL COMMENT 'URLã«ä»ä¸ããèå¥å',
`start` DATETIME NULL COMMENT 'éå§æ¥',
`finish` DATETIME NULL COMMENT 'çµäºæ¥',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0b0000:æªçæ 0b0001:å®äº 0b0010:å®è¡ä¸',
`created_at` DATETIME NOT NULL,
`created_by` INT UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NOT NULL,
`updated_by` INT UNSIGNED NOT NULL DEFAULT 0,
`deleted_at` DATETIME NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `identify_UNIQUE` (`identify` ASC))
ENGINE = InnoDB
COMMENT = 'ããã¸ã§ã¯ã';
CREATE TABLE IF NOT EXISTS `lists` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`project_id` BIGINT UNSIGNED NOT NULL,
`member_id` INT UNSIGNED NULL,
`todo` VARCHAR(300) NOT NULL,
`position` INT UNSIGNED NULL COMMENT 'é çª',
`priority` TINYINT UNSIGNED NULL DEFAULT 0 COMMENT 'åªå
度',
`time_limit` DATETIME NULL COMMENT 'ææ¥',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0b0000:æªçæ 0b0001:å®äº 0b0010:å®è¡ä¸',
`created_at` DATETIME NOT NULL,
`created_by` INT UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NOT NULL,
`updated_by` INT UNSIGNED NOT NULL DEFAULT 0,
`deleted_at` DATETIME NULL,
PRIMARY KEY (`id`),
INDEX `fk_list_project_idx` (`project_id` ASC),
INDEX `fk_list_member1_idx` (`member_id` ASC),
CONSTRAINT `fk_list_project`
FOREIGN KEY (`project_id`)
REFERENCES `project` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_list_member1`
FOREIGN KEY (`member_id`)
REFERENCES `member` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'Todo';
Composerã§ã¤ã³ã¹ãã¼ã«
DoctrineExtensionsBundleãComposerã§ã¤ã³ã¹ãã¼ã«ãã¾ãã DataFixuresBundleãå¾ã§ä½¿ãã®ã§ãä¸ç·ã«ã¤ã³ã¹ãã¼ã«ãã¾ãã
// composer.json
{
...
"require": {
...,
"stof/doctrine-extensions-bundle": "1.1.*",
"doctrine/doctrine-fixtures-bundle": "2.2.*",
}
}
$ composer install
ãã³ãã«ã®è¨å®
AppKernel.php
ã§ãã³ãã«ãæå¹åããã¾ãã
// app/AppKernel.php
public function registerBundles()
{
return array(
// ...
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
// ...
);
}
app/config/config.yml
ã§ä½¿ãããæ©è½ãè¨å®ãã¾ãã
--- app/config/old_config.yml
+++ app/config/new_config.yml
@@ -59,6 +60,19 @@
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
+ filters:
+ softdeleteable:
+ class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
+ enabled: true
+
+# Doctrine Extension
+stof_doctrine_extensions:
+ default_locale: ja_JP
+ orm:
+ default:
+ timestampable: true
+ softdeleteable: true
+ sortable: true
ã¨ã³ãã£ãã£ã®è¨å®
ãã§ã«åã¨ã³ãã£ãã£ã¯ã©ã¹ãèªåçæãã¦ããåæã§ãã¨ã³ãã£ãã£ã¯ã©ã¹ã«ã¢ããã¼ã·ã§ã³ã§æ©è½ãæå¹åããã¾ãã
<?php
--- Entity/old_Project.php
+++ Entity/new_Project.php
@@ -3,12 +3,14 @@
use Doctrine\ORM\Mapping as ORM;
+use Gedmo\Mapping\Annotation as Gedmo;
/**
* Project
*
* @ORM\Table(name="project", uniqueConstraints={@ORM\UniqueConstraint(name="identify_UNIQUE", columns={"identify"})})
* @ORM\Entity
+ * @Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class Project
{
@@ -60,6 +62,7 @@
* @var \DateTime
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
+ * @Gedmo\Timestampable(on="create")
*/
private $createdAt;
@@ -74,6 +77,7 @@
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime", nullable=false)
+ * @Gedmo\Timestampable(on="update")
*/
private $updatedAt;
$createdAt
ã¯ä½ææ¥ã$updatedAt
ã¯æ´æ°æ¥ã$deletedAt
ã¯å餿¥(ã½ããããªã¼ã)ã§è¨å®ãã¦ãã¾ã
<?php
--- Entity/old_Lists.php
+++ Entity/new_Lists.php
@@ -3,18 +3,20 @@
use Doctrine\ORM\Mapping as ORM;
+use Gedmo\Mapping\Annotation as Gedmo;
/**
* Lists
*
* @ORM\Table(name="lists", indexes={@ORM\Index(name="fk_list_project_idx", columns={"project_id"}), @ORM\Index(name="fk_list_member1_idx", columns={"member_id"})})
* @ORM\Entity
+ * @Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class Lists
{
@@ -27,14 +29,15 @@
* @ORM\Column(name="todo", type="string", length=300, nullable=false)
*/
private $todo;
/**
* @var integer
*
+ * @Gedmo\SortablePosition
* @ORM\Column(name="position", type="integer", nullable=true)
*/
private $position;
@@ -56,28 +59,30 @@
*/
private $status = '0';
/**
* @var \DateTime
*
* @ORM\Column(name="created_at", type="datetime", nullable=false)
+ * @Gedmo\Timestampable(on="create")
*/
private $createdAt;
/**
* @var integer
*
* @ORM\Column(name="created_by", type="integer", nullable=false)
*/
private $createdBy = '0';
/**
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime", nullable=false)
+ * @Gedmo\Timestampable(on="update")
*/
private $updatedAt;
@@ -90,14 +95,15 @@
* @ORM\Column(name="deleted_at", type="datetime", nullable=true)
*/
private $deletedAt;
/**
* @var \Project
*
+ * @Gedmo\SortableGroup
* @ORM\ManyToOne(targetEntity="Project")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
* })
*/
private $project;
$createdAt
ã¯ä½ææ¥ã$updatedAt
ã¯æ´æ°æ¥ã$deletedAt
ã¯å餿¥(ã½ããããªã¼ã)ã§è¨å®ãã¦ãã¾ã$position
ã¯ã½ã¼ãããéã«ãé çªãå ¥ãã¾ã$project
ã§@Gedmo\SortableGroup
ãè¨å®ãããã¨ã§ãProjectåä½ã§Listsã®ã½ã¼ããè¨å®ãã¦ããã¾ã
DataFixtureã§ãã¼ã¿ãå ¥ãã
追å ããæ©è½ãæ£ããåãã¦ãããã¯DataFixturesã使ã£ã¦ç¢ºèªãã¾ãã
<?php
// DataFixtures/ORM/LoadProjectData.php
namespace Shu1\SimulistBundle\Datafixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Shu1\SimulistBundle\Entity\Project;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* LoadProjectData Class
*
*/
class LoadProjectData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
/**
*
* @var ContainerInterface
*/
private $container;
/**
* (non-PHPdoc)
* @see Symfony\Component\DependencyInjection.ContainerAwareInterface::setContainer()
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
$entityManager = $this->container->get('doctrine')->getManager();
// auto incrementã®ãªã»ãã
$connection = $entityManager->getConnection();
$connection->exec('ALTER TABLE project AUTO_INCREMENT = 1;');
$project = new Project();
$project->setName('è²·ãç©ãªã¹ã');
$project->setIdentify('abcdefg');
$manager->persist($project);
$manager->flush();
$this->addReference('shopping_list', $project);
$project = new Project();
$project->setName('俺ã®ã¿ã¹ã¯');
$project->setIdentify('hijklmn');
$manager->persist($project);
$manager->flush();
$this->addReference('oreno_task', $project);
}
/**
* @return int
*/
public function getOrder()
{
return 1;
}
}
<?php
// DataFixtures/ORM/LoadListsData.php
namespace Shu1\SimulistBundle\Datafixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Shu1\SimulistBundle\Entity\Lists;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* LoadListsData Class
*
*/
class LoadListsData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
/**
*
* @var ContainerInterface
*/
private $container;
/**
* (non-PHPdoc)
* @see Symfony\Component\DependencyInjection.ContainerAwareInterface::setContainer()
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
$entityManager = $this->container->get('doctrine')->getManager();
// auto incrementã®ãªã»ãã
$connection = $entityManager->getConnection();
$connection->exec('ALTER TABLE lists AUTO_INCREMENT = 1;');
$lists = new Lists();
$lists->setProject($this->getReference('shopping_list'));
$lists->setTodo('ãè¶ãããããã«500ml');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('shopping_list'));
$lists->setTodo('å³å');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('shopping_list'));
$lists->setTodo('ãã®');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('shopping_list'));
$lists->setTodo('ç½è');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('shopping_list'));
$lists->setTodo('è±è');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('oreno_task'));
$lists->setTodo('é¨å±ãæé¤ãã');
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('oreno_task'));
$lists->setTodo('転å±
ãã¬ãã使ãã');
$lists->setTimeLimit(new \DateTime('2014-10-31 12:00'));
$manager->persist($lists);
$manager->flush();
$lists = new Lists();
$lists->setProject($this->getReference('oreno_task'));
$lists->setTodo('DVDãè¿å´ãã');
$lists->setTimeLimit(new \DateTime('2014-11-1 10:00'));
$manager->persist($lists);
$manager->flush();
}
/**
* @return int
*/
public function getOrder()
{
return 2;
}
}
ãã¼ã¿ããã¼ããã¾ãã
php app/console doctrine:fixtures:load --env dev
Careful, database will be purged. Do you want to continue Y/N ?y
> purging database
> loading [1] Shu1\SimulistBundle\Datafixtures\ORM\LoadProjectData
> loading [2] Shu1\SimulistBundle\Datafixtures\ORM\LoadListsData
ãã¼ã¿ãæ ¼ç´ãããã確èªãã¾ãã
mysql> select lists.id, project.identify, project.name, todo, position, lists.created_at, lists.updated_at, lists.deleted_at from lists inner join project on lists.project_id = project.id order by project_id, position;
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+------------+
| id | identify | name | todo | position | created_at | updated_at | deleted_at |
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+------------+
| 1 | abcdefg | è²·ãç©ãªã¹ã | ãè¶ãããããã«500ml | 0 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 2 | abcdefg | è²·ãç©ãªã¹ã | å³å | 1 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 3 | abcdefg | è²·ãç©ãªã¹ã | ãã® | 2 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 4 | abcdefg | è²·ãç©ãªã¹ã | ç½è | 3 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 5 | abcdefg | è²·ãç©ãªã¹ã | è±è | 4 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 6 | hijklmn | 俺ã®ã¿ã¹ã¯ | é¨å±ãæé¤ãã | 0 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 7 | hijklmn | 俺ã®ã¿ã¹ã¯ | 転å±
ãã¬ãã使ãã | 1 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 8 | hijklmn | 俺ã®ã¿ã¹ã¯ | DVDãè¿å´ãã | 2 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+------------+
8 rows in set (0.00 sec)
created_at
,updated_at
ã«æ¥ä»ãæ¿å
¥ããã¦ããã®ãåããã¾ããã¾ããposition
ããã¼ã¿æå
¥ããé ã«ã¤ã³ã¯ãªã¡ã³ãããã¦ããã®ã確èªã§ãã¾ãã
SoftDeleteableã¯ä»¥ä¸ã®ãããªã³ã¼ããå®è¡ãããã¨ã§ç¢ºèªã§ãã¾ãã
<?php
public function deleteAction(Request $request)
{
$id = '2';
$identify = 'abcdefg';
$entityManager = $this->getDoctrine()->getManager();
$queryBuilder = $entityManager->createQueryBuilder();
try {
$queryBuilder
->select('l, p')
->from('Shu1SimulistBundle:Lists', 'l')
->innerJoin('l.project', 'p')
->where('p.identify = :identify')
->andWhere('l.id = :id')
->setParameter('identify', $identify)
->setParameter('id', $id);
$list = $queryBuilder->getQuery()->getSingleResult();
} catch (\Exception $exception) {
$this->get('logger')->error($exception->getMessage());
return new Response('ng', 404);
}
// è¦ã¤ãããªãå ´å
if (!$list) {
return new Response('ng', 404);
}
$entityManager->remove($list);
$entityManager->flush();
return new Response('ok', 200);
}
å®è¡ããã¨ãid
ã2ã®ã¿ã¹ã¯(å³å)ã®deleted_at
ã«æ¥ä»ãå
¥ãã¾ãã
mysql> select lists.id, project.identify, project.name, todo, position, lists.created_at, lists.updated_at, lists.deleted_at from lists inner join project on lists.project_id = project.id order by project_id, position;
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+---------------------+
| id | identify | name | todo | position | created_at | updated_at | deleted_at |
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+---------------------+
| 1 | abcdefg | è²·ãç©ãªã¹ã | ãè¶ãããããã«500ml | 0 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 2 | abcdefg | è²·ãç©ãªã¹ã | å³å | 1 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | 2014-12-05 15:03:46 |
| 3 | abcdefg | è²·ãç©ãªã¹ã | ãã® | 2 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 4 | abcdefg | è²·ãç©ãªã¹ã | ç½è | 3 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 5 | abcdefg | è²·ãç©ãªã¹ã | è±è | 4 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 6 | hijklmn | 俺ã®ã¿ã¹ã¯ | é¨å±ãæé¤ãã | 0 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 7 | hijklmn | 俺ã®ã¿ã¹ã¯ | 転å±
ãã¬ãã使ãã | 1 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
| 8 | hijklmn | 俺ã®ã¿ã¹ã¯ | DVDãè¿å´ãã | 2 | 2014-12-05 15:03:13 | 2014-12-05 15:03:13 | NULL |
+----+----------+--------------------+--------------------------------+----------+---------------------+---------------------+---------------------+
8 rows in set (0.00 sec)
æå¾ã«
StofDoctrineExtensionsBundleãè¨å®ãããã¨ã§ç´°ããå¶å¾¡ãããªãã¦ãæ¸ãã®ã§ã¨ã¦ã便å©ã§ãã
ä»ã®æ©è½ãä½¿ãæ©ä¼ãããã°ã¾ãããã°ã«æ¸ãããã¨æãã¾ãã