Read out the value of the related discriminator map - symfony

I have an Order entity, what has a property called $name. This property is an entity called Heading and has a one to one relation to the order table.
Also, this heading has a discrimiatormap, here are the annotation:
/**
* #ORM\Entity
* #ORM\Table(name="Heading")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorMap({
* "simple"="SimpleName",
* "separated"="SeparatedName",
* "company"="CompanyName"
* })
*/
Foreign keys are set properly.
What I want to achieve, to get the name of the user.
I am trying to get a column, called fullName with the name of user.
This how I try:
$queryBuilder->leftJoin(
'e.name',
'h',
);
$queryBuilder->addSelect('
(
CASE
WHEN h INSTANCE OF '.SimpleName::class.' THEN h.name
WHEN h INSTANCE OF '.CompanyName::class.' THEN h.company
WHEN h INSTANCE OF '.SeparatedName::class." THEN CONCAT(h.first_name, ' ', h.last_name)
ELSE ''
END
)
AS fullName
");
It fails, it says, Utils\\Model\\Heading\\Heading has no field or association named name.
I've tried THEN h.SimpleName, but I also get no field for SimpleName.
If I just using a string there like ssss, ccccc, spspsps, then it's cool, I am getting the result, so the CASE WHEN ELSE is works.
How can I reference to those fields? What should I wrote after the THEN in the 3 cases?
CompanyName, SimpleName and SeparatedName are extends the Heading class.
Here is the CompanyName class for example:
/**
* #ORM\Entity
*/
class CompanyName extends Heading
{
/**
* #ORM\Column(type="string", nullable=true)
*/
private ?string $companyName = null;
public static function create(?string $companyName = null): self
{
$object = new self();
$object->companyName = $companyName;
return $object;
}
public function __toString(): string
{
return $this->companyName ?? '';
}
public function getType(): string
{
return 'company';
}
public function getCompanyName(): ?string
{
return $this->companyName;
}
public function setCompanyName(?string $companyName): self
{
$this->companyName = $companyName;
return $this;
}
}

Meanwhile I've changed the h to orderHeading, and here is the solution.
protected function createQueryBuilder(): QueryBuilder
{
$queryBuilder = parent::createQueryBuilder();
$queryBuilder->leftJoin(
'e.name',
'orderHeading'
);
$queryBuilder->leftJoin(SeparatedName::class, 'separatedName', Join::WITH, 'orderHeading.id = separatedName.id')
->leftJoin(SimpleName::class, 'simpleName', Join::WITH, 'orderHeading.id = simpleName.id')
->leftJoin(CompanyName::class, 'companyName', Join::WITH, 'orderHeading.id = companyName.id');
$queryBuilder->resetDQLPart('groupBy');
$queryBuilder->addGroupBy('e');
return $queryBuilder;
}
.....
protected function createCountQueryBuilder(): QueryBuilder
{
$queryBuilder = parent::createCountQueryBuilder();
$queryBuilder->leftJoin(
'e.name',
'orderHeading'
);
$queryBuilder->leftJoin(SeparatedName::class, 'separatedName', Join::WITH, 'orderHeading.id = separatedName.id')
->leftJoin(SimpleName::class, 'simpleName', Join::WITH, 'orderHeading.id = simpleName.id')
->leftJoin(CompanyName::class, 'companyName', Join::WITH, 'orderHeading.id = companyName.id');
return $queryBuilder;
}
and after that you can reference to these columns as
$propertyMap['fullName'] = new ColumnSqlDescriptor('
CASE
WHEN orderHeading INSTANCE OF '.SimpleName::class.' THEN simpleName.name
WHEN orderHeading INSTANCE OF '.CompanyName::class.' THEN companyName.companyName
WHEN orderHeading INSTANCE OF '.SeparatedName::class." THEN CONCAT(separatedName.firstName, ' ', separatedName.lastName)
ELSE 'NA'
END
");

Related

Symfony/Doctrine - QueryException - [Semantical Error] line 0, col 48 near 'pizza-mia': Error: 'pizza' is not defined

I want to make this query in SQLite using DQL
//// src/Repository/PostRepository.php
public function hasPreviousPost($id,$slug): array
{
$em = $this->getEntityManager();
$query = $em->createQuery('SELECT p FROM App\Entity\Post p WHERE p.id < '.$id.' AND p.slug = '.$slug);
$posts = $query->getResult();
return $posts;
}
And I call this function from the PostController like that
//// src/Controller/PostController.php
$posts = $this->getDoctrine()->getRepository(Post::class)->findAll();
foreach ($posts as $post){
$hasPreviousPost = $this->getDoctrine()->getRepository(Post::class)->hasPreviousPost($post->id(),$post->Slug());
}
But when I run the code, I get this error
" [Semantical Error] line 0, col 48 near 'pizza-mia': Error: 'pizza' is not defined. "
pizza-mia is what $slug contains, a string.
This is my Post entity.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
$countPosts = 0;
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
* #ORM\Table(name="post")
*/
class Post
{
/**
* #ORM\Column(type="integer")
* #ORM\Column(unique=true)
*/
private $id;
/**
* #ORM\Id
* #ORM\Column(type="text")
* #ORM\Column(unique=true)
*/
private $postId;
/**
* #ORM\Column(type="text")
*/
private $slug;
/**
* #ORM\Column(type="text")
*/
private $message;
/**
* #ORM\Column(type="boolean",nullable=true)
*/
private $isScheduled;
/**
* #ORM\Column(type="integer")
*/
private $scheduledPublishTime;
function __construct($postId,$slug,$message)
{
global $countPosts;
$countPosts++;
$this->postId=$postId;
$this->id=$countPosts;
$this->slug=$slug;
$this->message=$message;
$this->isScheduled=false;
$this->scheduledPublishTime=0;
}
public function PostId(): ?string
{
return $this->postId;
}
public function id(): ?int
{
return $this->id;
}
public function Slug(): ?string
{
return $this->slug;
}
public function Message(): ?string
{
return $this->message;
}
public function IsScheduled(): ?bool
{
return $this->isScheduled;
}
public function changeIsScheduled(?bool $isScheduled): self
{
$this->isScheduled = $isScheduled;
return $this;
}
public function ScheduledPublishTime(): ?int
{
return $this->scheduledPublishTime;
}
public function changeScheduledPublishTime(int $scheduledPublishTime): self
{
$this->scheduledPublishTime = $scheduledPublishTime;
return $this;
}
}
Any help will be appreciated.
Try it this way.
$query = $em->createQuery('SELECT p FROM App\Entity\Post p WHERE p.id < :id AND p.slug = :slug');
$query->setParameter('id', $id);
$query->setParameter('slug', $slug);
:id and :slug stand for a named parameter
My strong assumption is that you simply are missing the ' ' to have your $slug defined as string in your query.
But using parameters like above makes it more readable as well.
Find an explanation about named or numbered parameters in the documentation

symfony2 restrict model based on user - upgrade from symfony1

I'm building a multi-tenant application. In Symfony1 I would restrict access to data by accessing the user details and extending the createQuery function:
class PersonTable extends Doctrine_Table{
public function createQuery($alias = '')
{
$query = parent::createQuery($alias);
try {
$user = sfContext::getInstance()->getUser();
}catch(Exception $e){
if ($e->getMessage() == 'The "default" context does not exist.'){
return $query;
}else{
throw $e;
}
}
if ($user->hasGroup('Team1')){
//all good
}else if ($user->hasGroup('Team2')){
$user_id = $user->getGuardUser()->getStaff()->getId();
$alias = $query->getRootAlias();
$time = date('Y-m-d H:i:s',time());
$query->andWhere("$alias.type='type1' and pe.assigned_psw_id");
}
$query->orderBy('name asc');
return $query;
}
}
I know there are downsides to accessing the user object through sfContext in sf1, but this method seemed superior to others, as you can't "forget" to secure a controller against wrong user access.
How can I achieve the same in Symfony2?
I have solved this problem the following way.
Standardise how EntityRepository is fetched among controllers:
public function getUserRestrictedRepository($entity, $em = null )
{
$securityContext = $this->get( 'security.context' );
if (!$em){
$em = $this->getDoctrine()->getManager();
}
return $em
->getRepository( 'MyBundle:' . $entity )
->setSecurityContext( $securityContext );
}
Add a trait to provide queries with injected security query:
trait UserRestrictedEntityRepository {
private $securityContext;
/**
* #return mixed
*/
public function getSecurityContext()
{
return $this->securityContext;
}
/**
* #param mixed $securityContext
*/
public function setSecurityContext($securityContext)
{
$this->securityContext = $securityContext;
return $this;
}
/**
* #return mixed
*/
public function getUser()
{
return $this->getSecurityContext()->getToken()->getUser();
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
function secureQueryWithUser($alias, $qb)
{
$qb->where("1=0");
}
function appendOrderBy($qb, $orderBy)
{
$first = true;
foreach ($orderBy as $field => $dir) {
if (!$dir) $dir = 'asc';
if ($first) {
$qb->orderBy('c.' . $field, $dir);
$first = false;
}else{
$qb->addOrderBy('c.' . $field, $dir);
}
}
}
public function createUnrestrictedQueryBuilder($alias)
{
return parent::createQueryBuilder($alias);
}
/**
* Creates a new QueryBuilder instance that is prepopulated for this entity name.
*
* #param string $alias
*
* #return QueryBuilder
*/
public function createQueryBuilder($alias, $indexBy=NULL)
{
if ($this->getUser()) {
$qb = $this->_em->createQueryBuilder()
->select($alias)
->from($this->_entityName, $alias);
if (isset($this->defaultOrder) && $this->defaultOrder){
$this->appendOrderBy($qb, $this->defaultOrder);
}
if ($this->getUser()->isSuperAdmin()){
return $qb;
}else{
return $this->secureQueryWithUser($alias, $qb);
}
}else{
throw new Exception('Run setUser() before querying ' . $this->getName() .' model.');
}
}
/**
* Finds all entities in the repository.
*
* #return array The entities.
*/
public function findAll()
{
return $this->findBy(array());
}
/**
* Finds entities by a set of criteria.
*
* #param array $criteria
* #param array|null $orderBy
* #param int|null $limit
* #param int|null $offset
*
* #return array The objects.
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
{
$qb = $this->createQueryBuilder('c');
foreach ($criteria as $fkey => $fval){
$qb->andWhere($fkey.' = :'.$fval);
}
if ($limit){
$qb->setMaxResults($limit);
}
if ($offset){
$qb->setFirstResult($offset);
}
$query = $qb->getQuery();
return $query->getResult();
}
}
Implement query additions based on user access in the EnityRepository
class FarmerRepository extends EntityRepository
{
use UserRestrictedEntityRepository;
private $name = 'Farmer';
private $defaultOrder = array('name' => 'asc');
function secureQueryWithUser($alias, $qb)
{
if ($this->getSecurityContext()->isGranted( 'ROLE_CLINIC_ADMIN' )) {
return $qb
->innerJoin("$alias.vet", 'v')
->innerJoin("v.clinic", "cl")
->innerJoin("cl.VetsOfClinic", "vc")
->andWhere('vc.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else if ($this->getSecurityContext()->isGranted( 'ROLE_VET' )){
return $qb
->innerJoin("$alias.vet", 'v')
->andWhere('v.user_id= :userid')
->setParameter('userid', $this->getUser()->getId());
}else{
return $qb
->where("$alias.user_id= :userid")
->setParameter('userid', $this->getUser()->getId());
}
}
}

how to get the uploded filenames of a form in the controller

i used the following code in the controller inorder to get the filenames of the uploaded files
My controller is
class uploadController extends Controller
{
public function uploadAction(Request $request)
{
$id= $_GET['id'];
$user = new attachments();
$form = $this->createFormBuilder($user)->add('files','file',array("data_class" => null,"attr"=>array("multiple" =>"multiple",)))->getForm();
$formView = $form->createView();
$formView->getChild('files')->set('full_name','files[]');
if ($request->getMethod() == 'POST')
{
$em = $this->getDoctrine()->getManager();
$data = $form["files"]->getData();
}
}
when i print the $data it is not giving the filenames of uploaded files it is returning the null values
my entity is:
use Symfony\Component\HttpFoundation\File\UploadedFile;
class attachments
{
private $id;
/**
* #var integer
* #ORM\Column(name="user", type="integer", nullable=false)
* #ORM\ManyToOne(targetEntity="users", inversedBy="annotations")
*/
protected $userId;
/**
* #var string
*
* #Assert\File(maxSize="6000000")
* #ORM\Column(name="files", type="array", length=255, nullable=true)
*/
public $files=array();
public function __construct()
{
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set userId
*
* #param integer $userId
* #return attachments
*/
public function setUserId($userId)
{
$this->userId = $userId;
return $this;
}
/**
* Set files
* #param object $files
*
* #return attachments
*/
public function setFiles($files)
{
$this->files = $files;
}
/**
* Get files
*
* #return object
*/
public function getFiles()
{
return $this->files;
}
public function uploadFiles()
{
// the files property can be empty if the field is not required
if (null === $this->files)
{
return;
}
else
{
$this->files->move($this->getUploadRootDir(), $this->files->getClientOriginalName());
}
$this->setFiles($this->files->getClientOriginalName());
}
/**
* Get userId
*
* #return integer
*/
public function getUserId()
{
return $this->userId;
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir() . DIRECTORY_SEPARATOR . $this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir() . DIRECTORY_SEPARATOR . $this->path;
}
protected function getUploadRootDir()
{
return __DIR__ . '/../../../../web/'. $this->getUploadDir();
}
protected function getUploadDir()
{
return 'uploads/';
}
}
Uploaded Files in Symfony2 are of type Symfony/Component/HttpFoundation/File/UploadedFile.
You can get the original client name ( php will rename files when putting them into php_upload_tmp_dir ) with:
$file->getClientOriginalName();
... move the file to a new location with:
$file->move('path/to/your_file', 'new_name.jpg');
You can not use the assert File Constraint for an array.
* #Assert\File(maxSize="6000000")
*/
protected $files = array();
Therefore you need the All constraint.
Furthermore you can't just call the move method on an array or collection... you will have to loop over the collection/array.
$this->files->move('..') // this is never going to work...
Use an array collection and create a property for your uploaded files if thats what you want.
protected $files;
protected $uploadedFiles;
public function __construct()
{
$this->files = new ArrayCollection;
$this->uploadedFiles = new Array();
}
If you want to transform your Doctrine Collection of UploadedFile entities into an Array do the following:
$collection = $entity->getFiles();
$array = $collection->toArray();
But whatever you're trying to do ... better use OOP instead of arrays like you're attempting here.

Combining #Gedmo\NestedTree and #ORM\UniqueEntity

I'm creating a folder structure implemented with the NestedTree behaviour.
Furthermore, I don't want that two folders may have the same name if they are siblings.
For this, I use the combination of #UniqueEntity and #UniqueConstraint annotations, but it does not work.
First my entity (stripped to the minimum since it is 100% identical to the NestedTree defaults) :
/**
* #ORM\Entity
* #Gedmo\Tree(type="nested")
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
* #UniqueEntity(fields={"parent", "name"})
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(name="uniq_url", columns={"parent_id", "name"})})
*/
class Folder
{
/**
* #ORM\Column(type="string", nullable=false)
*/
protected $name;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Folder", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $parent;
}
First try (ignoreNull = true)
When I create two folders with the same name, I have an integrity constraint violation, meaning that the #UniqueConstraints in the database worked but that the #UniqueEntity didn't :
Integrity constraint violation: 1062 Duplicate entry 'name_of_folder' for key 'uniq_url'
Second try (ignoreNull = false)
I also tried with the ignoreNull key set to false (the default is true) :
#UniqueEntity(fields={"parent", "name"}, ignoreNull=false)
but then I get this error :
Warning: ReflectionProperty::getValue() expects parameter 1 to be object, null given in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php line 670
I've nailed the error down to these lines in Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator :
$criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity);
if ($constraint->ignoreNull && null === $criteria[$fieldName]) {
return;
}
if ($class->hasAssociation($fieldName)) {
/* Ensure the Proxy is initialized before using reflection to
* read its identifiers. This is necessary because the wrapped
* getter methods in the Proxy are being bypassed.
*/
$em->initializeObject($criteria[$fieldName]);
$relatedClass = $em->getClassMetadata($class->getAssociationTargetClass($fieldName));
//problem
$relatedId = $relatedClass->getIdentifierValues($criteria[$fieldName]);
if (count($relatedId) > 1) {
throw new ConstraintDefinitionException(
"Associated entities are not allowed to have more than one identifier field to be " .
"part of a unique constraint in: " . $class->getName() . "#" . $fieldName
);
}
$criteria[$fieldName] = array_pop($relatedId);
}
The problem appears on the line marked with //problem. It appears that $criteria[$fieldName] === null is the reason of the error.
So here I am, not knowing what to do...
Does anybody have an idea on what's going on ?
Thank you.
There is no easy way to get out of this situation.
I finally went my own way and created a validator :
Entity
/**
* #ORM\Entity(repositoryClass="Ibiz\DoctrineExtensionsBundle\Entity\Repository\NestedTreeRepository")
* #Gedmo\Tree(type="nested")
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(name="uniq_url", columns={"parent_id", "name"})})
* #IbizAssert\UniquePath("getName")
*/
class Folder
{
/**
* #ORM\Column(type="string", nullable=false)
*/
protected $name;
public function getName()
{
return $this->name;
}
}
Validator/Constraints/UniquePath.php
namespace Ibiz\DoctrineExtensionsBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class UniquePath extends Constraint
{
public $em = null;
public $errorMethod = null;
public $message = 'The name "%name%" already exists.';
public $service = 'ibiz.validator.unique_path';
public function validatedBy()
{
return $this->service;
}
public function getRequiredOptions()
{
return array('errorMethod');
}
public function getDefaultOption()
{
return 'errorMethod';
}
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
Validator/Constraints/UniquePathValidator.php
namespace Ibiz\DoctrineExtensionsBundle\Validator\Constraints;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class UniquePathValidator extends ConstraintValidator
{
private $registry;
public function __construct(ManagerRegistry $registry)
{
$this->registry = $registry;
}
public function validate($entity, Constraint $constraint)
{
if ($constraint->errorMethod === null)
{
throw new ConstraintDefinitionException('ErrorMethod should be set');
} else if (!is_string($constraint->errorMethod)) {
throw new UnexpectedTypeException($constraint->errorMethod, 'string');
}
if ($constraint->em) {
$em = $this->registry->getManager($constraint->em);
} else {
$em = $this->registry->getManagerForClass(get_class($entity));
}
$className = $this->context->getClassName();
$repo = $em->getRepository($className);
$count = $repo->getSameNameSiblingsCount($entity);
if ($count != 0) {
$this->context->addViolation($constraint->message, array('%name%' => $entity->{$constraint->errorMethod}()));
}
}
}
Entity/Repository/NestedTreeRepository.php
namespace Ibiz\DoctrineExtensionsBundle\Entity\Repository;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository as BaseRepository;
class NestedTreeRepository extends BaseRepository
{
public function getSameNameSiblingsCountQueryBuilder($node)
{
$meta = $this->getClassMetadata();
if (!$node instanceof $meta->name) {
throw new InvalidArgumentException("Node is not related to this repository");
}
$config = $this->listener->getConfiguration($this->_em, $meta->name);
$qb = $this->_em->createQueryBuilder();
$qb->select($qb->expr()->count('n.id'))
->from($config['useObjectClass'], 'n');
if ($node->getParent() === null) {
$qb->where($qb->expr()->andx(
$qb->expr()->eq('n.name', ':name'),
$qb->expr()->isNull('n.parent')
))
->setParameters(array(
'name' => $node->getName(),
));
} else {
$qb->leftJoin('n.parent', 'p')
->where($qb->expr()->andx(
$qb->expr()->eq('n.name', ':name'),
$qb->expr()->eq('p.name', ':parent')
))
->setParameters(array(
'name' => $node->getName(),
'parent' => $node->getParent()->getName(),
));
}
return $qb;
}
public function getSameNameSiblingsCountQuery($node)
{
return $this->getSameNameSiblingsCountQueryBuilder($node)->getQuery();
}
public function getSameNameSiblingsCount($node)
{
return $this->getSameNameSiblingsCountQuery($node)->getSingleScalarResult();
}
}

Symfony2 and ParamConverter(s)

Accessing my route /message/new i'm going to show a form for sending a new message to one or more customers. Form model has (among others) a collection of Customer entities:
class MyFormModel
{
/**
* #var ArrayCollection
*/
public $customers;
}
I'd like to implement automatic customers selection using customers GET parameters, like this:
message/new?customers=2,55,543
This is working now by simply splitting on , and do a query for getting customers:
public function newAction(Request $request)
{
$formModel = new MyFormModel();
// GET "customers" parameter
$customersIds = explode($request->get('customers'), ',');
// If something was found in "customers" parameter then get entities
if(!empty($customersIds)) :
$repo = $this->getDoctrine()->getRepository('AcmeHelloBundle:Customer');
$found = $repo->findAllByIdsArray($customersIds);
// Assign found Customer entities
$formModel->customers = $found;
endif;
// Go on showing the form
}
How can i do the same using Symfony 2 converters? Like:
public function newAction(Request $request, $selectedCustomers)
{
}
Answer to my self: there is not such thing to make you life easy. I've coded a quick and dirty (and possibly buggy) solution i'd like to share, waiting for a best one.
EDIT WARNING: this is not going to work with two parameter converters with the same class.
Url example
/mesages/new?customers=2543,3321,445
Annotations:
/**
* #Route("/new")
* #Method("GET|POST")
* #ParamConverter("customers",
* class="Doctrine\Common\Collections\ArrayCollection", options={
* "finder" = "getFindAllWithMobileByUserQueryBuilder",
* "entity" = "Acme\HelloBundle\Entity\Customer",
* "field" = "id",
* "delimiter" = ",",
* }
* )
*/
public function newAction(Request $request, ArrayCollection $customers = null)
{
}
Option delimiter is used to split GET parameter while id is used for adding a WHERE id IN... clause. There are both optional.
Option class is only used as a "signature" to tell that converter should support it. entity has to be a FQCN of a Doctrine entity while finder is a repository method to be invoked and should return a query builder (default one provided).
Converter
class ArrayCollectionConverter implements ParamConverterInterface
{
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
function apply(Request $request, ConfigurationInterface $configuration)
{
$name = $configuration->getName();
$options = $this->getOptions($configuration);
// Se request attribute to an empty collection (as default)
$request->attributes->set($name, new ArrayCollection());
// If request parameter is missing or empty then return
if(is_null($val = $request->get($name)) || strlen(trim($val)) === 0)
return;
// If splitted values is an empty array then return
if(!($items = preg_split('/\s*'.$options['delimiter'].'\s*/', $val,
0, PREG_SPLIT_NO_EMPTY))) return;
// Get the repository and logged user
$repo = $this->getEntityManager()->getRepository($options['entity']);
$user = $this->getSecurityContext->getToken()->getUser();
if(!$finder = $options['finder']) :
// Create a new default query builder with WHERE user_id clause
$builder = $repo->createQueryBuilder('e');
$builder->andWhere($builder->expr()->eq("e.user", $user->getId()));
else :
// Call finder method on repository
$builder = $repo->$finder($user);
endif;
// Edit the builder and add WHERE IN $items clause
$alias = $builder->getRootAlias() . "." . $options['field'];
$wherein = $builder->expr()->in($alias, $items);
$result = $builder->andwhere($wherein)->getQuery()->getResult();
// Set request attribute and we're done
$request->attributes->set($name, new ArrayCollection($result));
}
public function supports(ConfigurationInterface $configuration)
{
$class = $configuration->getClass();
// Check if class is ArrayCollection from Doctrine
if('Doctrine\Common\Collections\ArrayCollection' !== $class)
return false;
$options = $this->getOptions($configuration);
$manager = $this->getEntityManager();
// Check if $options['entity'] is actually a Dcontrine one
try
{
$manager->getClassMetadata($options['entity']);
return true;
}
catch(\Doctrine\ORM\Mapping\MappingException $e)
{
return false;
}
}
protected function getOptions(ConfigurationInterface $configuration)
{
return array_replace(
array(
'entity' => null,
'finder' => null,
'field' => 'id',
'delimiter' => ','
),
$configuration->getOptions()
);
}
/**
* #return \Doctrine\ORM\EntityManager
*/
protected function getEntityManager()
{
return $this->container->get('doctrine.orm.default_entity_manager');
}
/**
* #return \Symfony\Component\Security\Core\SecurityContext
*/
protected function getSecurityContext()
{
return $this->container->get('security.context');
}
}
Service definition
arraycollection_converter:
class: Acme\HelloBundle\Request\ArrayCollectionConverter
arguments: ['#service_container']
tags:
- { name: request.param_converter}
It's late, but according to latest documentation about #ParamConverter, you can achieve it follow way:
* #ParamConverter("users", class="AcmeBlogBundle:User", options={
* "repository_method" = "findUsersByIds"
* })
you just need make sure that repository method can handle comma (,) separated values

Resources