Doctrine 2 filters are a very powerful feature allowing you to easily modify every request sent to the database. This comes in very handy when you need to implement a soft delete filter: a filter which will load only records from database which are not marked as deleted.
With ZF2 dependency injection (DI) support, this is really easy to configure.
Soft delete is when a record is not deleted from the database, but rather just marked as deleted using a boolean field. Doctrine filters provide an easy and secure way to implement this.
As soft delete requires a column to mark records as deleted, you need the relevant entities to implement an interface called SoftDeleteInterface, which defines getters and setters for a field called ‘deleted’. This ensures your entity will be consistent with the database model, and also allows the filter to recognise records with soft delete functionality.
You then create a DeletedFilter, which checks entities to see if they have implemented that interface, and if so, filters out any which have been set to deleted (deleted = 1). You can then add this filter to the configuration, which guarantees that only records with deleted=0 can be loaded anywhere in the system.
Why should you do this? Well, the simple answer is that this can increase your website’s security: if you give the db user a limited set of permissions (select, insert, update), you can be sure that nobody can drop / delete / truncate content, even by accident.
To implement this, add the following to the module.config.php configuration for Doctrine:
'doctrine' => array(
'configuration' => array(
'orm_default' => array(
'filters' => array(
'soft_delete' => 'Admin\Filter\DeletedFilter'
)
)
),
'driver' => array(........
Then define a filter, similar to one which you can find in the Doctrine documentation. This uses reflection to check if the entity implements the SoftDeleteInterface:
<?php
namespace Admin\Filter;
use Doctrine\ORM\Query\Filter\SQLFilter;
use Doctrine\ORM\Mapping\ClassMetadata;
use Admin\Entity\SoftDeleteInterface;
class DeletedFilter extends SQLFilter
{
/**
* Gets the SQL query part to add to a query.
*
* @return string The constraint SQL if there is available, empty string otherwise
*/
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
// Check if the entity implements the SoftDelete interface
if (!in_array("Admin\Entity\SoftDeleteInterface", $targetEntity->reflClass->getInterfaceNames())) {
return "";
}
return $targetTableAlias.'.deleted = '.SoftDeleteInterface::STATUS_NOT_DELETED;
}
}
Finally, the soft delete interface:
<?php
namespace Admin\Entity;
/**
* Interface to support soft delete
*/
Interface SoftDeleteInterface
{
const STATUS_NOT_DELETED = 0;
const STATUS_DELETED = 1;
/**
* @param int $deleted
*/
public function setDeleted($deleted);
public function getDeleted();
}
And last step – you need to enable filter. Add to Module.php:
public function onBootstrap(MvcEvent $e){
$entityManager = $e->getApplication()->getServiceManager()->get('doctrine.entitymanager.orm_default');
$filter = $entityManager->getFilters()->enable("soft_delete");
}