Symfony form query_buider and entity repository - symfony

I'm trying to create a form with data in collection type depending on the user being logged. I'm following this chapter of the Symfony cookbook.
Everything works fine when the query_builder option is a closure where I get my data from DQL. As the data need to be fetched from different location in code, I would prefer to define the query in the Repository class.
Here is the function in my repository :
public function findOwnedBy($user) {
$query = $this->getEntityManager()->createQuery("SELECT l FROM MyBundle:Article a JOIN a.owndBy u WHERE u.id = :userId");
$query->setParameters(array("userId"=>$user->getId()));
return $query->getResult();
}
This function works when called in a Controller and return an array of Article. Here is a snippet of the symfony doc :
$formOptions = array(
'class' => 'Acme\DemoBundle\Entity\User',
'multiple' => false,
'expanded' => false,
'property' => 'fullName',
'query_builder' => function(EntityRepository $er) use ($user) {
// build a custom query, or call a method on your repository (even better!)
},
);
When I put a call to my Repository function in the query_builder, I get an error : Expected argument of type "Doctrine\ORM\QueryBuilder", "array" given, which I can understand because my Repository returns an array of Entity, not a QueryBuilder.
I don't want to duplicate code and create a new QueryBuilder in the Form. What is the best practice to use the query from the Repository ? I was thinking of having two function in the repository, one returning an array and the other returning the QueryBuilder, but the comment in Symfony doc "or call a method on your repository (even better!)" let me think there's better way for this case.

It should be easy. Do the following:
public function queryOwnedBy($user) {
$query = $this->createQueryBuilder('a')
->from('MyBundle:Article', 'a')
->innerJoin('a.owndBy', 'u')
->where('u.id = :id')
->setParameter('id', $user->getId());
return $query;
}
public function findOwnedBy($user) {
return $this->queryOwnedBy($user)
->getQuery()
->getResult();
}
Then in the form builder:
$formOptions = array(
'class' => 'Acme\DemoBundle\Entity\User',
'multiple' => false,
'expanded' => false,
'property' => 'fullName',
'query_builder' => function(EntityRepository $er) use ($user) {
return $er->queryOwnedBy($user);
},
);
EDIT
Thank's for ncatnow and unagi I've changed the previous functions to return the querybuilder

I just did a little fix of saamorim answer. The working code would be something like this:
public function queryOwnedBy($user) {
$query = $this->createQueryBuilder("u")
->where('u.id = :id')
->setParameter('id', $user->getId());
return $query;
}
public function findOwnedBy($user) {
return $this->queryOwnedBy($user)
->getQuery()
->getResult();
}

Related

doctrine queryBuilder where IN collection

On my entity I have an array collection of users
/**
* #ORM\ManyToMany(targetEntity="\UserBundle\Entity\User", mappedBy="acr_groups")
*/
protected $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
In my FormType I want to filter out those groups wherein current user is a member:
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where(':user IN (g.users)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
My problem is obviously on this line:
$qb->where(':user IN (g.users)');
How can I use my collection of users as the argument for the IN()?
Try below code
$user = array(12,211,612,84,63,23); // Assuming User Ids whose groups you want to retrive
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) {
$qb = $er->createQueryBuilder('g');
$qb->innerJoin('g.users', 'u'); // Inner Join with users
$qb->where('u.id IN (:user)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
I have tried it in symfony 2.3 with doctrine2. You can use select function with createQueryBuilder() to get specific columns.
$q = $this->createQueryBuilder('v')
->select('v')
->andWhere('v.workingHours IN (:workingHours)')
->setParameter('workingHours', $workingHours);
From : Doctrine 2 WHERE IN clause using a collection of entities
Or according to doctrine documentation : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html#the-expr-class
To insert IN condition in query builder with doctrine you can use expr()
$qb->add('select', new Expr\Select(array('u')))
->add('from', new Expr\From('User', 'u'))
->add('where', $qb->expr()->orX(
$qb->expr()->eq('u.id', '?1'),
$qb->expr()->like('u.nickname', '?2')
))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
Syntaxe of IN :
$qb->expr()->in('u.id', array(1, 2, 3))
Also, Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1
I ended up turning thing around a bit after unsuccessfully attempting some of your solutions. I manually created an array of the IDs I wanted.
There is probably a native way of doing this, seems like a pretty standard thing... this works however.
// 1. to inject user entity into this builder first make a construct function (remember to inject it from controller!)
function __construct($user)
{
$this->user = $user;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->user; // 2. instantiate the variable we created in our construct above
//create group list array
$groupList = $this->user->getACRGroups();
$gla = array();
foreach ($groupList as $g) {
$gla[] = $g->getId();
};
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($gla) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where('g.id IN (:gla)');
$qb->setParameters( array('gla' => $gla) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))

Symfony EasyAdminBundle: Filter entities in assotiation field

I have a form with assotation field type (list of related entities).
What I've been trying to achieve is to filter this list on "newAction" form (create new entity).
For example, following screen below:
There is a Survey entity with field "User".
There is Department entity with field "Survey" (#ORM\ManyToOne) where the User choose a survey.
You can see two available surveys but I want to display only the first one, because its User field value is the same as current user.
It is confusing, because I can't find values passed to the Survey field when I debuging.
Best way is override default controller and apply query builder for form like this.
YML:-
easy_admin:
entities:
Department:
class: YourBundle\Entity\Department
controller: YourBundle\Controller\Admin\Model\DepartmentController
In DepartmentController:-
<?php
namespace YourBundle\Controller\Admin\Model;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use JavierEguiluz\Bundle\EasyAdminBundle\Controller\AdminController;
class DepartmentController extends AdminController
{
public function createDepartmentEntityFormBuilder($entity, $view)
{
$formBuilder = parent::createEntityFormBuilder($entity, $view);
$user = $this->get('security.token_storage')->getToken()->getUser();
$formBuilder->add('survey', EntityType::class, [
'class' => 'YourBundle\Entity\Survey',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->where('s.user = :user')
->setParameter('user', $user);
},
]);
return $formBuilder;
}
}
My solution:
$formBuilder = parent::createEntityFormBuilder($entity, $view);
if (!$this->get('security.authorization_checker')->isGranted('ROLE_SUPER_ADMIN')) {
$user = $this->get('security.token_storage')->getToken()->getUser();
$promoter = $this->getDoctrine()
->getRepository(Promoter::class)
->findByUser($user);
$queryBuilder = $this->getDoctrine()
->getRepository(Customer::class)
->getActiveByPromoterQueryBuilder($promoter);
$formBuilder->add(
'customers', EntityType::class, [
'class' => Customer::class,
'query_builder' => $queryBuilder,
"attr" => ["class" => "form-control select2", "data-widget" => "select2"],
'by_reference' => false,
'multiple' => true,
'required' => false
]
);
}
return $formBuilder;
}
According to 'vendor/easycorp/easyadmin-bundle/src/Resources/views/default/includes/_select2_widget.html.twig'
We only need to add data-widget attribute.

Symfony2 Form query_builder - allow null

In Symfony2 forms, when trying to get entities, Symfony expects to receive QueryBuilder object, but sometimes there are no entities returned. In that case, an error message appears:
Expected argument of type "Doctrine\ORM\QueryBuilder", "NULL" given
How to make query_builder to allow option that there are no entities available.
$builder
->add('client', 'entity', array(
'class' => 'Faktura\FakturaBundle\Entity\Client',
'query_builder' => function(\Web\MyBundle\Repository\ClientRepository $er) use ($company){
return $er->getClients($company);
))
;
ClientRepository.php
public function getClients($company)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.company = :company')
->setParameter('company', $company)
->getQuery();
return $qb->getResult();
}
Actually, it's just basic $er->findBy(array('company' => $company)) method
but I use custom getClients() method
Your Closure should return QueryBuilder object, not results of it.
Your ClientRepository should look like:
public function getClients($company)
{
$qb = $this->getClientsQueryBuilder($company);
return $qb->getQuery()->getResult();
}
public function getClientsQueryBuilder($company)
{
return $this->createQueryBuilder('c')
->select('c')
->where('c.company = :company')
->setParameter('company', $company);
}
And then you need to use getClientQueryBuilder in your Closure.
$builder
->add('client', 'entity', array(
'class' => 'Faktura\FakturaBundle\Entity\Client',
'query_builder' => function(\Web\MyBundle\Repository\ClientRepository $er) use ($company){
return $er->getClientsQueryBuilder($company);
))
;

how to call function of entity repository in form type in symfony2

i want to call function in form type class. function generate array and is written in entity repository class. using that array i will generate dynamic form field.
here is entity repository class function.
public static $roleNameMap = array(
self::ROLE_SUPER_ADMIN => 'superAdmin',
self::ROLE_MANAGEMEN => 'management',
self::ROLE_MANAGERS => 'manager',
self::ROLE_IT_STAFF => 'itStaff',
self::ROLE_CS_CUSTOMER => 'csCustomer',
self::ROLE_CS => 'cs',
self::ROLE_DEALER => 'dealer',
self::ROLE_ACCOUNT_STAFF => 'accountStaff',
self::ROLE_BROKER_USER => 'staff',
);
public function getGroupListArray()
{
$qb = $this->createQueryBuilder('g')
->orderBy('g.hierarchy','ASC');
$query = $qb->getQuery();
$groupList = $query->execute();
$roleNameMap = array();
foreach ($groupList as $role){
$roleNameMap[$role->getId()] = $role->getRole();
}
return $roleNameMap;
}
below is my form builder class where i want to call above entity repository function.
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('routeId', 'hidden');
foreach (GroupListRepository::$roleNameMap as $key=>$value){
$builder->add($value, 'checkbox',array('label' => '', 'required' => false,));
}
}
i am able to get static variable as show in above code but, i have confusion that how should i access repository function in form builder class in symfony2.
thanks in advance.
It's not available in the form builder, and it's normally not necessary. It's also not really how Symfony forms work. For what it looks like you're wanting to do, you could try something like this. It will create a list of checkboxes corresponding to a list of roles.
$builder->add(
'roles',
'entity',
array(
'class' => 'Acme\DefaultBundle\Entity\Group',
'expanded' => true,
'multiple' => true,
'property' => 'role', // Or use __toString()
'query_builder' => function ($repository) {
return $repository->createQueryBuilder('g')
->orderBy('g.hierarchy', 'ASC');
}
)
);
See http://symfony.com/doc/master/reference/forms/types/entity.html.
If you really need the repository in the form builder, then create the form type as a service and inject the entity manager with the DIC. Or just pass it directly into the form type when you create it.
You do not need to create a query builder function and can use a query from the Repository like so:
In the form:
'query_builder' => function(MyCustomEntityRepository $ttr) {
return $ttr->queryForCustomResultsWithQueryBuilder();
}
In the repository:
public function queryForCustomResultsWithQueryBuilder($published=true) {
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
return $queryBuilder->select('tt')
->from('ifm\CustomBundle\Entity\CustomEntity','tt')
->where('tt.published = ?1')
->orderBy('tt.code', 'ASC')
->setParameters(array(1=>$published))
;
}
Note that the queryForCustomResultsWithQueryBuilder returns a QueryBuilder not a result. If you also need a result you'll need to write a find function in the reposiory.

passing data from controller to Type symfony2

if i show a field of type "entity" in my form, and i want to filter this entity type based on a argument I pass from the controller, how do i do that.. ?
//PlumeOptionsType.php
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('framePlume', 'entity', array(
'class' => 'DessinPlumeBundle:PhysicalPlume',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('pp')
->where("pp.profile = :profile")
->orderBy('pp.index', 'ASC')
->setParameter('profile', ????)
;
},
));
}
public function getName()
{
return 'plumeOptions';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Dessin\PlumeBundle\Entity\PlumeOptions',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'plumeOptions_item',
);
}
}
and inside the controller, i create the form :
i have that argument that i need to pass in my action code:
$profile_id = $this->getRequest()->getSession()->get('profile_id');
...
and then i create my form like this
$form = $this->createForm(new PlumeOptionsType(), $plumeOptions);
the $plumeOptions is just a class to persist. But it has a one-to-one relationship with another class called PhysicalPlume. Now, when i want to display the 'framePlume' in my code, i want to show a filtered PhysicalPlume entity.
You can pass parameters to the form class as follows:
//PlumeOptionsType.php
protected $profile;
public function __construct (Profile $profile)
{
$this->profile = $profile;
}
Then use it in the query_builder of your buildForm:
$profile = $this->profile;
$builder->add('framePlume', 'entity', array(
'class' => 'DessinPlumeBundle:PhysicalPlume',
'query_builder' => function(EntityRepository $er) use ($profile) {
return $er->createQueryBuilder('pp')
->where("pp.profile = :profile")
->orderBy('pp.index', 'ASC')
->setParameter('profile', $profile)
;
},
));
And finally in your controller:
// fetch $profile from DB
$form = $this->createForm(new PlumeOptionsType($profile), $plumeOptions);
You can use $plumeOptions to pass everything your argument, but you'll need to add a getDefaultOptions() in PlumeOptionsType to specify the default value for your option.
See for instance https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php to see what this method should look like.

Resources