I'm building a Saas / Multitenant application using Symfony 2. I've created a Doctrine event-subscriber to add and update the owner of a row, the user who created it, the user who modified it, timestamps and so.
And now I need to implement some kind of filter so when a user is logged in, he only can see data from his company. My first though was using a Doctrine preLoad event, but this event doesn't exist... As far as I know, I must use Doctrine filters, isn't it? If so, how can this filter access user data to read the company id? Must I inject it using the Dependency Injection? Is there any standard way to accomplish my goal?
UPDATE
What I'm looking for is to create some kind of Doctrine plugin/hook so everytime I call any function that fetch data from the database (find, findOneBy, etc), and the entity I'm fetching implements a particular interface, an extra 'AND company_id=:id' SQL sequence is added to the generated query, so neither the controller or the model receives data from other companies.
For this you can use a DoctrineFilter
Link from official doc Doctrine2 SQL Filters
namespace Rwmt\Bundle\RwmtBundle\DoctrineFilters;
use Doctrine\ORM\Mapping\ClassMetaData,
Doctrine\ORM\Query\Filter\SQLFilter;
class MultiTenantFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
// Check if the entity implements the MultiTenant interface
if (!$targetEntity->reflClass->implementsInterface('Rwmt\Bundle\RwmtBundle\Entity\MultiTenant')) {
return "";
}
return $targetTableAlias.'.tenant_id = ' . $this->getParameter('tenantId');
}
}
And to set the parameter tenantId used in the filter you must enable the filter and set the parameter
$filter = $em->getFilters()->enable('multi_tenant');
$filter->setParameter('tenantId', $tenant->getId(), 'integer');
As for the MultiTenant interface is just something for the entities to implement
namespace Rwmt\Bundle\RwmtBundle\Entity;
use Rwmt\Bundle\RwmtBundle\Entity\Tenant;
interface MultiTenant
{
public function setTenant(Tenant $tenant);
public function getTenant();
}
Filtering in Doctrine2 is simple. Just assign a filter function to a variable, then send that variable as a parameter through the filter() method included in the ArrayCollection class.
$closure = function($list_item) use($user) {
return $list_item->belongsToSameCompanyThatEmploys($user) === true;
};
$filtered_array_collection = $arrayYouWantToFilter->filter($closure);
In this example, you'd have to have previously defined a method belongsToSameCompanyThatEmploys($user)
in list_item's class that returns true
if it belongs to the same company that the user works for.
UPDATE
You need to also indicate that the filter function should use the local variable user, since it won't otherwise due to having its own scope.