Edited at

Symfony3で論理削除の導入


概要

仕事で論理削除を実装する必要が出てきたので調べたついでに残しておく


環境


  • PHP → 7.1.23

  • symfony/symfony → v3.4.18

  • doctrine/orm → v2.6.2

  • gedmo/doctrine-extensions → v2.4.36

  • stof/doctrine-extensions-bundle → v1.3.0


設定


Doctrine拡張のインストール

$ composer require stof/doctrine-extensions-bundle


バンドルの有効化


app/AppKernel.php

<?php 

class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = [
// ...

new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),

// ...
];
{
}



Configに設定


app/config/config.yml

doctrine:

orm:
# フィルターを利用したい場合はこれも書く
filters:
softdeleteable:
class: 'Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter'
enabled: true

# 論理削除設定
stof_doctrine_extensions:
default_locale: '%locale%'
orm:
default:
softdeleteable: true



Entityの準備


src/AppBundle/Entity/Product.php

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;

/**
* Class Product
* @package AppBundle\Entity
* @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
* @ORM\Table(name="product")
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=true)
*/

class Product
{
/**
* @var int
*
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/

private $id;

/**
* @var \DateTime
*
* @ORM\Column(type="datetime", nullable=true)
*/

private $deletedAt;

/**
* Get id.
*
* @return int
*/

public function getId()
{
return $this->id;
}

/**
* @return \DateTime
*/

public function getDeletedAt()
{
return $this->deletedAt;
}

/**
* @param \DateTime $deletedAt
*/

public function setDeletedAt($deletedAt)
{
$this->deletedAt = $deletedAt;
}
}



DBに反映

$ bin/console doctrine:schema:update --force


使用例(フィルターと論理削除)


src/AppBundle/Controller/Admin/ProductController.php

<?php 

/**
* Class ProductController
* @package AppBundle\Controller\Admin
* @Route("/admin/product")
*/

class ProductController extends Controller
{
/**
* 製品一覧(フィルター適用例)
* @Route("/", name="admin_product_list")
* @Method("GET")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/

public function listAction(Request $request)
{
// 製品一覧取得
/** @var EntityManager $em */
$em = $this->getDoctrine()->getManager();
// 論理削除したデータも含めて取得したい場合はフィルターを適用する(soft-deleteableは古い記述の仕方っぽい)
$em->getFilters()->disable('softdeleteable');
$products = $em->getRepository(Product::class)->findAll();

dump($products)

// ...
}
// ...

/**
* 製品削除(論理削除例)
*
* @Route("/{id}/delete", name="admin_product_delete")
* @Method("DELETE")
* @param Request $request
* @param Product $product
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/

public function deleteAction(Request $request, Product $product)
{
// ...

// Entityで指定さえしておけば特別な記述なく論理削除できる
$em = $this->getDoctrine()->getManager();
$em->remove($product);
$em->flush();

// 現バージョンは2回削除を実行する事で物理削除となる(バグ?)
// $em->remove($product);
// $em->flush();

// 正常動作はこちらのはず
// $em->getFilters()->disable('softdeleteable');
// $em->remove($product);
// $em->flush();

// ...
}
}



注意点


  • 論理削除したデータを取得しないのはORMを利用する場合だけなのでDQLとクエリビルダの時はwhereで指定する


参考文献