I have a many to many relationships in my project (user_role, grades, user_role_grades). But I also have a requirement not to delete any data from my db. So, I have add a status column to the table, that connecting 2 tables to create many to many relationship. Now I want on
$userRole->getGrades()
get only those records, that in unite table (user_role_grades) has no status "0". For those, I`m trying to use doctrine sql filter.
namespace Bis\MpBundle\Filter;
use \Doctrine\ORM\Mapping\ClassMetaData;
class UserRoleGradeFilter extends \Doctrine\ORM\Query\Filter\SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if("Bis\DefaultBundle\Entity\UserRoleGrade" == $targetEntity->name){
return $targetTableAlias . '.status != 0';
}
return '';
}
}
So, it is called, for Bis\DefaultBundle\Entity\UserRole, but not for Bis\DefaultBundle\Entity\UserRoleGrade entity. Have anyone any ideas?
Or may be you have some other ideas, how I can do that?
I don't think this is possible, because it appends directly SQL. Even if you'd try sth like SQL injection:
return $targetTableAlias . '.status != 0)) LEFT join the_other_table ON '
. $targetTableAlias . '.grades HAVING the_other_table.status = 0 ((';
It would probably collapse on statement like (())
You can call $targetEntity->getAssociationMappings()['yourFieldName']
and if joinTable
key exist that mean your have manyToMany
relation. Where yourFieldName
your field with ManyToMany
relation.
Correct table alias you can get from Doctrine\ORM\Query\SqlWalker::getSQLTableAlias()
.
I get SqlWalker
with debug_backtrace
, not an elegant solution but better I've not found.
/**
* Get SqlWalker with debug_backtrace
*
* @return null|SqlWalker
*/
protected function getSqlWalker()
{
$caller = debug_backtrace();
$caller = $caller[2];
if (isset($caller['object'])) {
return $caller['object'];
}
return null;
}
Full implementation of my addFilterConstraint
method
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if (empty($this->reader)) {
return '';
}
// The Doctrine filter is called for any query on any entity
// Check if the current entity is "pool aware" (marked with an annotation)
$poolAware = $this->reader->getClassAnnotation(
$targetEntity->getReflectionClass(),
PoolAware::class
);
if (!$poolAware) {
return '';
}
if (!$poolId = $this->getParameter('poolId')) {
return '';
}
$fieldName = $poolAware->getFieldName();
if (empty($fieldName)) {
return '';
}
if (!$sqlWalker = $this->getSqlWalker()) {
return '';
}
if (!isset($targetEntity->getAssociationMappings()[$fieldName])) {
return '';
}
$mapping = $targetEntity->getAssociationMappings()[$fieldName];
if (isset($mapping['joinColumns'])) {
// oneToMany relation detected
$table = $targetEntity->getTableName();
$columnName = $mapping['joinColumns'][0]['name'];
$dqlAlias = constant($targetEntity->getName() . '::MNEMO');
} elseif (isset($mapping['joinTable'])) {
// manyToMany relation detected
$dqlAlias = constant($mapping['targetEntity'] . '::MNEMO');
$component = $sqlWalker->getQueryComponent($dqlAlias);
// Only main entity in query is interesting for us,
// otherwise do not apply any filter
if ($component['parent']) {
return '';
}
$table = $mapping['joinTable']['name'];
$columnName = $mapping['joinTable']['inverseJoinColumns'][0]['name'];
} else {
return '';
}
$tableAlias = ($sqlWalker instanceof BasicEntityPersister)
? $targetTableAlias // $repository->findBy() has been called
: $sqlWalker->getSQLTableAlias($table, $dqlAlias);
$query = sprintf('%s.%s = %s', $tableAlias, $columnName, $this->getConnection()->quote(poolId));
return $query;
}
All our Doctrine models have MNEMO constant which is simple name of a model. Abc\Module\Model\Product
has MNEMO product
. This MNEMO is equivalent of _alias
in Repository class. That's why we apply this value to $dqlAlias
Full code explanation you can read here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With