Make security available for doctrine onFlush within functional test - symfony

I'm actually testing my api code written with:
symfony 4
api-platform
FOS User
JWT
I use codeption for my tests and everything is ok so far.
For several entities, I fire onFlush doctrine callback and it's working just fine when authenticated from my front application in react.
At this point I get my authenticated user in the callback via an injected security component.
However when doing the same things via codeception, even if onFlush is fired, I'm not able to retrieve my user neither the token via the security injection.
I tried to inject the token instead, also the entire service container, none has worked.
This is my OnFlush class:
{
/**
* #var Security
*/
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function onFlush(OnFlushEventArgs $args): void
{
$user = $this->security->getUser();
...
And here how I set my authorization header in codeception test:
$I->haveHttpHeader('Authorization', 'Bearer ' . $token);
$I->sendPUT(
'/entity/uuid.json',
[
'attribute' => $value
]
);
I would like to get the user having the specified token whe executing the test in the callback.
PS: Before executing the PUT test, I did the same thing with GET and just got the related entities, when I remove Authorization header I do get all users entities. It seems that it's not working only in callback.
Thanks

After a lot research, it's obviously a codeception problem.
I ended up making this particular test with phpunit as codeception couldn't load the service container in doctrine events.
If you try to edit your services.yaml file and to execute your tests, it works on first time as the service container is re-built (re-cached).
But once cached, it will always return an empty container (without tokenstrorage, security, ...).
Creating a helper method to provide the user wouldn't work neither, I'll leave the code here in case of need:
/**
* Create user or administrator and set auth cookie to client
*
* #param string $user
* #param string $password
* #param bool $admin
*/
public function setAuth(string $user, string $password, bool $admin = false): void
{
/** #var Symfony $symfony */
try {
$symfony = $this->getModule('Symfony');
} catch (ModuleException $e) {
$this->fail('Unable to get module \'Symfony\'');
}
/** #var Doctrine2 $doctrine */
try {
$doctrine = $this->getModule('Doctrine2');
} catch (ModuleException $e) {
$this->fail('Unable to get module \'Doctrine2\'');
}
$user = $doctrine->grabEntityFromRepository(User::class, [
'username' => $user
]);
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$symfony->grabService('security.token_storage')->setToken($token);
/** #var Session $session */
$session = $symfony->grabService('session');
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$symfony->client->getCookieJar()->set($cookie);
}
Creating a phpunit test with below code would do the job just fine:
/**
* #param string $method
* #param string $url
* #param array $content
* #param bool $authorization
*/
protected static function performRequest (string $method, string $url, array $content = [], $authorization = false): void
{
$headers = [
'CONTENT_TYPE' => 'application/json',
];
if ($authorization)
{
$headers = array_merge($headers, [
'HTTP_AUTHORIZATION' => 'Bearer ' . self::$token
]);
}
self::$client->request(
$method,
'/api/' . $url,
[],
[],
$headers,
json_encode($content)
);
}

I got exactly the same problem. Here is my solution:
<?php
use Codeception\Stub;
use Codeception\Module\Doctrine2;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\Security;
class YourEventSubscriberCest
{
/**
* #var Security
*/
protected Security $security;
/**
* #param FunctionalTester $I
*/
public function _before(FunctionalTester $I, Doctrine2 $doctrine2)
{
$user = new User();
$security = Stub::makeEmpty(Security::class, [
'getUser' => $user,
]);
$backup = $this->replaceSecurity($doctrine2, $security);
if ($backup instanceof Security) {
$this->security = $backup;
}
}
public function _after(FunctionalTester $I, Doctrine2 $doctrine2)
{
$this->replaceSecurity($doctrine2, $this->security);
}
protected function replaceSecurity(Doctrine2 $doctrine2, object $newSecurity): ?object
{
$listeners = $doctrine2->_getEntityManager()->getEventManager()->getListeners('onFlush');
foreach ($listeners as $listener) {
if ($listener instanceof YourEventSubscriber) {
$reflection = new \ReflectionObject($listener);
$property = $reflection->getProperty('security');
$property->setAccessible(true);
$oldSecurity = $property->getValue($listener);
$property->setValue($listener, $newSecurity);
return $oldSecurity;
}
}
}
}

Related

The App\Security\LoginFormAuthenticator::getUser() method must return a UserInterface. You returned Softonic\GraphQL\Response

I'm trying to customize the login with a graphql query to load the User data and it shows me that error.
I use php bin / console make: auth and it works correctly when I query the $ user variable from MySQL but when I load the $ user variable from GRAPHQL it shows me that error.
This is the code:
public function getUser($credentials, UserProviderInterface $userProvider)
{
if ($credentials["csrf_token"] != "") {
$client = new GH();
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
$result = $client->request('POST', 'https://graphql.clientecliente.com/get-token', [
'json' => [
'usuario' => $credentials["email"],
'clave' => $credentials["password"]
]
]);
$data = $result->getBody()->getContents();
$objetodata=(json_decode($data));
$token = $objetodata->token;
}
//Conexion servidor GraphQL
$servidor = $this->params->get('enlace_graphql');
//Codigo debe salir desde la base de datos
$codigoAuth = $token;
$options = [
'headers' => [
'Authorization' => 'Bearer ' . $codigoAuth,
],
];
$client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);
$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
totalCount
OBT_USUARIOS {
CODIGO_USUARIO
CLAVE_USUARIO
CODIGO_EMPRESA
CODIGO_CLIENTE
CODIGO_PASAJERO
ES_ADMINISTRADOR
}
}
}
QUERY;
$variables1 = [
'limit' => 10,
'offset' => 0,
'CODIGO_USUARIO' => $credentials["email"]
];
//$user = $this->entityManager->getRepository(Usuario::class)->findOneBy(['email' => $credentials['email']]);
$user = $client->query($gpl1,$variables1);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('El usuario no existe');
}
return $user;
}
Maybe, Anything idea?
Update.
Now create the custom user provider. I use this command: php bin/console make:user;
<?php
//src/Security/UserProvider.php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
//Consulta API
use GuzzleHttp\Client as GH;
use GuzzleHttp\Pool;
//Consulta GraphQL
use Softonic\GraphQL;
use Softonic\GraphQL\Client;
use Softonic\GraphQL\ClientBuilder;
use function GuzzleHttp\json_decode;
class UserProvider implements UserProviderInterface
{
/**
* Symfony calls this method if you use features like switch_user
* or remember_me.
*
* If you're not using these features, you do not need to implement
* this method.
*
* #return UserInterface
*
* #throws UsernameNotFoundException if the user is not found
*/
public function loadUserByUsername($username)
{
$client = new GH();
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
$result = $client->request('POST', 'https://graphql.cliente.com.ec/get-token', [
'json' => [
'usuario' => 'test',
'clave' => 'test1234',
]
]);
$data = $result->getBody()->getContents();
$objetodata=(json_decode($data));
//var_dump($objetodata->token);
//exit;
$token = $objetodata->token;
$servidor = "https://graphql.cliente.com/graphql";
//Codigo debe salir desde la base de datos
//$codigoAuth = $token;
$codigoAuth= "12345678912345678mlvIjoiT0JUX0NjIxOTI1ODN9.8dNiZI6iZsYS0plVU0fuqFlhkTDSOt9OFy5B-WZiRmk";
//var_dump($codigoAuth);
//exit;
//echo $codigoAuth; exit;
$options = [
'headers' => [
'Authorization' => 'Bearer ' . $codigoAuth,
],
];
$client = \Softonic\GraphQL\ClientBuilder::build($servidor, $options);
$gpl1 = <<<'QUERY'
query ($limit: Int!, $offset: Int!, $CODIGO_USUARIO: String) {
obt_usuarios(limit: $limit, offset: $offset, CODIGO_USUARIO: $CODIGO_USUARIO) {
totalCount
OBT_USUARIOS {
CODIGO_USUARIO
CLAVE_USUARIO
CODIGO_EMPRESA
CODIGO_CLIENTE
CODIGO_PASAJERO
ES_ADMINISTRADOR
}
}
}
QUERY;
$variables1 = [
'limit' => 10,
'offset' => 0,
'CODIGO_USUARIO' => 'test'
];
$user=$client->query($gpl1,$variables1);
//var_dump($user);exit;
//$username=$user;
return $user;
//throw new \Exception('TODO: fill in loadUserByUsername() inside '.__FILE__);
}
/**
* Refreshes the user after being reloaded from the session.
*
* When a user is logged in, at the beginning of each request, the
* User object is loaded from the session and then this method is
* called. Your job is to make sure the user's data is still fresh by,
* for example, re-querying for fresh User data.
*
* If your firewall is "stateless: true" (for a pure API), this
* method is not called.
*
* #return UserInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}
// Return a User object after making sure its data is "fresh".
// Or throw a UsernameNotFoundException if the user no longer exists.
throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
return $this->loadUserByUsername($user->getUsername());
}
/**
* Tells Symfony to use this provider for this User class.
*/
public function supportsClass($class)
{
return User::class === $class;
}
}
config/packages/security.yaml:
providers:
app_user_provider:
id: App\Security\UserProvider
firewalls:
secured_area:
anonymous: true
form_login:
login_path: loginanterior
check_path: login_check
default_target_path: index
provider: app_user_provider
remember_me: true
logout:
path: logout
target: index
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
src/Security/User.php
<?php
namespace App\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
private $email;
private $roles = [];
/**
* #var string The hashed password
*/
private $password;
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
However, after this it does not show me any errors or anything. Just go back to the login again
You have to map your Softonic\GraphQL\Response to your User model. This should be done in your custom UserProvider so Authenticator doesn't know where the user actually comes from. You can also look at existing user providers code for inspiration.
Thanks to the help of Ion Bazan
I understood that I had to make a custom UserProvider.
Final steps encoder correction
config/packages/security.yaml
encoders:
App\Security\User:bcrypt
GraphQL result mapping on USER object by sending user, ROL and password (must be sent in the encryption correctly)
App/src/Security/UserProvider.php
$userData=$client->query($gpl1,$variables1)->getData();
//var_dump($userData);
$user = new User();
$user->setEmail($userData['obt_usuarios']["OBT_USUARIOS"][0]["CODIGO_USUARIO"]);
$user->setRoles(array("ROLE_USER"));
$user->setPassword('$2a$10$JSMRqSFX9Xm1gAbc/YzVcu5gETsPF8HJ3k5Zra/RZlx4IXfadwmW.');
return $user;
}
/**
* Refreshes the user after being reloaded from the session.
*
* When a user is logged in, at the beginning of each request, the
* User object is loaded from the session and then this method is
* called. Your job is to make sure the user's data is still fresh by,
* for example, re-querying for fresh User data.
*
* If your firewall is "stateless: true" (for a pure API), this
* method is not called.
*
* #return UserInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}
// Return a User object after making sure its data is "fresh".
// Or throw a UsernameNotFoundException if the user no longer exists.
//throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
return $this->loadUserByUsername($user);
}
With this finally it worked

Symfony4 encode password in Security Folder

I'm doing a authentication with guard feature. Problem is than I have to put a password to my User, he don't have to know this password so I choose to generate a random password. Problem is than I'm not in a controller so I can't use UserPasswordEncoderInterface ... So I'm looking for some help here.
I give you some code :
public function getUser($credentials, UserProviderInterface $userProvider)
{
/**
* #var FacebookUser $facebookUser
*/
$facebookUser = $this->getFacebookClient()
->fetchUserFromToken($credentials);
$email = $facebookUser->getEmail();
$user = $this->em->getRepository('App:User')
->findOneBy(['email' => $email]);
if (!$user) {
$user = new User();
$user->setEmail($facebookUser->getEmail());
$user->setName($facebookUser->getFirstName());
$user->setLastName($facebookUser->getLastName());
$user->setRoles(["ROLE_USER"]);
//TODO HASH PASSWORD
$user->setPassword(bin2hex(random_bytes(80)));
$this->em->persist($user);
$this->em->flush();
}
return $user;
}
and the method from controller
/**
* After going to Facebook, you're redirected back here
* because this is the "redirect_route" you configured
* in config/packages/knpu_oauth2_client.yaml
* #Route("/connect/facebook/check", name="connect_facebook_check")
*
* #return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function connectCheckAction() {
if (!$this->getUser()) {
return new JsonResponse(array('status' => false, 'message' => "User not found!"));
} else {
// $em = $this->getDoctrine()->getManager();
//
// $user = $this->getUser();
// $password = bin2hex(random_bytes(80));
// $hash = $encoder->encodePassword($user, $password);
// $user->setPassword($hash);
//
// $em->persist($user);
// $em->flush();
return $this->redirectToRoute('default');
}
}
You can inject EncoderFactoryInterface by constructor:
/**
* #var EncoderFactoryInterface
*/
private $securityEncoderFactory;
public function __construct(EncoderFactoryInterface $securityEncoderFactory)
{
$this->securityEncoderFactory = $securityEncoderFactory;
}
And then use:
$encoder = $this->securityEncoderFactory->getEncoder($user);
$encoder->encodePassword($user, $password);
You can just use the PHP's function password_hash to hash your randomly generated password. See the documentation here

Lifecycle callbacks not working

I'm currently trying out Symfony 4, but I am having some problems with events triggered by database action (prePersist, preUpdate...)
With Symfony 3, I used to use EntityListener to accomplish this, but I found them really convoluted in Symfony 4 documentation. But I also discovered the LifecycleCallbacks, that I used like this:
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
//Attributes and other functions not included for the sake of clarity, but if I use them, consider that they exist
/**
* #ORM\PrePersist
*/
public function setPostSlug()
{
$title = $this->getPostTitle();
$title = strtolower($title);
$keywords = preg_split("/[\s,']+/", $title);
$slug = implode('-', $keywords);
dump($slug);
$this->$slug = $slug;
return $this;
}
}
My post are created through a Symfony form, and before persistence, I want to break down the title I gave to my post in a standardized string that I will use in my URLs to access said post. Unfortunately, the event never trigger on persistence, despite the slug being generated correctly. I tried to do the operation both on prePersist and postPersist events, but none worked. I searched the issue, and saw that LifecycleCallbacks needed a cache clear to be taken into account, but doing so didn't help.
Here is the action responsible for the post creation, if that might help:
/**
* #Route("/admin/create/post", name="admin-create-post")
* #param Request $request
*/
public function createPost(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$post = $form->getData();
$em->persist($post);
$em->flush();
$this->redirectToRoute('main');
}
return $this->render('admin/new_post.html.twig', array(
'form' => $form->createView()
));
}
Would you know the source of the problem, or which other tools I could use to obtain the desired result?
Thanks in advance.
I handle complex Lifecycle with EventListener
for this .. do :
# services.yml
AppBundle\EventListener\YourListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
// YourListener.php
namespace AppBundle\EventListener;
class YourListener {
/**
* #param LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args): void
{
$post = $args->getEntity();
if ($post instanceof Post) {
// Do your job
}
}
}
But I use symfony EventListenerSubscriber Like this:
/**
* This needs to be set through passed argument in case of accident duplicate
*
* #ORM\PrePersist()
*/
public function setTrackingNumber()
{
$this->trackingNumber = NumberCreator::randomStringWithNDigits(self::TRACKING_DIGIT_COUNT);
}
so I think you need do that in your slug setter like this
/**
* #ORM\PrePersist
*/
public function setSlug()
{
$title = $this->getPostTitle();
$title = strtolower($title);
$keywords = preg_split("/[\s,']+/", $title);
$slug = implode('-', $keywords);
dump($slug);
$this->$slug = $slug;
return $this;
}
I think method name is issue ... I hope this is help to you

am new with symfony and i need some advices

I have a problem regarding a Symfony application, I want to take as input the "username" or "id" for my controller , and receive information that is in my table "user" and also 2 other table for example : A user has one or more levels , and also it has points must earn points to unlock a level , I want my dan Home page display the username and the level and extent that it has , I jn am beginner and not come to understand the books symfony that I use, I work with PARALLEL " symfony_book " and " symfony_cook_book " and also tutorial youtube May I blocks , here is the code for my cotroler
"
/**
* #Route("/{id}")
* #Template()
* #param $id=0
* #return array
*/
public function getUserAction($id)
{
$username = $this->getDoctrine()
->getRepository('voltaireGeneralBundle:FosUser')
->find($id);
if (!$username) {
throw $this->createNotFoundException('No user found for id '.$id);
}
//return ['id' => $id,'username' => $username];
return array('username' => $username);
}
and I have to use the relationship among classes
use Doctrine\Common\Collections\ArrayCollection;
class Experience {
/**
* #ORM\OneToMany(targetEntity="FosUser", mappedBy="experience")
*/
protected $fosUsers;
public function __construct()
{
$this->fosUsers = new ArrayCollection();
}
}
and
class FosUser {
/**
* #ORM\ManyToOne(targetEntity="Experience", inversedBy="fosUsers")
* #ORM\JoinColumn(name="experience_id", referencedColumnName="id")
*/
protected $fosUsers;
}
and i have always an error
In Symfony you cannot return an array in Action function!, Action function must always return a Response object...So if you want to return data to browser in Symfony, Action function have to return a string wrapped up in Response object.
In your controller code, to return the array to browser, You can serialize an array to JSON and send it back to browser:
public function getUserAction($id)
{
$username = $this->getDoctrine()
->getRepository('voltaireGeneralBundle:FosUser')
->find($id);
if (!$username) {
throw $this->createNotFoundException('No user found for id '.$id);
}
return new Response(json_encode(array('username' => $username)));
}
I suggest you to read more about HTTP protocol , PHP, and Symfony.

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