How can I call method of entity in a repository querybuilder - symfony

I have this scenario:
a entity Person, a repository for Person and a select form type.
I should take in my select form only active person.
In the entity there is a public method "isActive" that check if a Person have the permission to access in my private area. This method return true or false and is not a column in the db table, it is calculated.
I need to access this flag from my querybuilder in the Person repository.
Is it possible?
below the code of my querybuilder in the repository.
public function getQueryBuilderForEventRegistration()
{
$queryBuilder = $this->createQueryBuilder('e')->orderBy('e.surname', 'asc')->addOrderBy('e.name', 'asc');
return $queryBuilder;
}
and the public method in the entity Person tha t i have to access:
public function getIsActive()
{
if (empty($this->getUser()))
{
return false;
}
if (!$this->getUser()->isEnabled())
{
return false;
}
/* #var $service \Siderweb\SubscriptionBundle\Entity\UserService */
foreach ($this->getUser()->getServices() as $service)
{
if (!$service->getIsExpired())
{
return true;
}
}
return false;
}
and my type:
$builder->add('personExist', 'entity', array(
'class' => 'MyAppUserBundle:Person',
'property' => 'name',
'required' => false,
'multiple' => false,
'mapped' => false,
'empty_value' => '-- New person --',
'query_builder' => function(PersonRepository $repo) use ($options) {
return $repo->getQueryBuilderForEventRegistration();
}
))
as suggested I edit my repository like this:
public function getQueryBuilderForEventRegistration(Company $company = null, Event $event = null, $emailFilter = null)
{
$queryBuilder = $this->createQueryBuilder('e')->orderBy('e.surname', 'asc')->addOrderBy('e.name', 'asc');
$people = $queryBuilder->getQuery()->execute();
$peopleToShow = array();
foreach ($people as $person)
{
if ($person->getIsActive())
{
array_push($peopleToShow, $person);
}
}
return $peopleToShow;
}
but now I don't know how to put this array in my typeForm. Any idea?

Okay, so you can't call entity method on the query builder, but you can on the query results. Doctrine will hydrate your entity objects with the data that is returned from the database.
Once you have your results you can call the isActive() method on the hydrated entities.
The way that you're trying to implement this (getting the query builder for the form), you will need an isActive column in your database table and add a 'where' clause like so:
public function getQueryBuilderForEventRegistration()
{
$queryBuilder = $this->createQueryBuilder('e')
->where('e.isActive', true)
->orderBy('e.surname', 'asc')
->addOrderBy('e.name', 'asc');
return $queryBuilder;
}

It is not possible to call a Custom PHP method a SQL query, so Doctrine's QueryBuilder does not allow that. You will need either to have isActive as a database field (or custom method), or to reproduce you getIsActive method with QueryBuilder conditions.

You could use the filter function to filter the array on the template.
In my case I have an entity that has a canBeProcessed method ( which depends on many things ).
So, when I want to show in the index method of the controller only those records that
"canBeProcessed" I use this :
{% for emitted_note in pagination|filter( emitted_note => emitted_note.canBeProcessed == true ) %}
<tr><td>{{emitted_note.data}}</td><tr>
{% endfor %}
Here's the twig documentation.
https://twig.symfony.com/doc/2.x/filters/filter.html
Regards.

Related

Symfony choice type array : Unable to transform value for property path : Expected an array

First of all, I'm a new user of Symfony
And actually I'm customing my form EasyAdmin with some fields and I have an issue with this one :
ChoiceField::new('villa')->setChoices($villasChoices)->allowMultipleChoices()
I get this error because of the allowMultipleChoices() func :
Unable to transform value for property path "villa": Expected an array.
My field is actually a collection type, That's why I have this error, there is my entity
#[ORM\OneToMany(mappedBy: 'Name', targetEntity: Villa::class)]
private Collection $Villa;
public function __construct()
{
$this->Villa = new ArrayCollection();
}
/**
* #return Collection<int, Villa>
*/
public function getVilla(): Collection
{
return $this->Villa;
}
How can I remplace Collection type by Array ?
Try to use AssociationField instead of ChoiceField.
AssociationField list entities automatically, while ChoiceField is made to pass array manually.
->setFormTypeOption('multiple', true)
Will do the trick for multiple choice, if your property allow multiples values (OneToMany, ManyToMany)
You must have a form like this:
$form = $this->createFormBuilder($yourEntity)
->add('villa', EntityType::class, [
'class' => Villa::class
'multiple' => true
])
;
Set the 'villa' item with EntityType::class and set in the options 'multiple' => true.
In your Villa entity you must set a __tostring method like this:
public function __toString(): string
{
return $this->name; //name is a string value
}

Handling Symfony Collection violations for user data

I'm writing an API that will take in a JSON string, parse it and return the requested data. I'm using Symfony's Validation component to do this, but I'm having some issues when validating arrays.
For example, if I have this data:
{
"format": {
"type": "foo"
}
}
Then I can quite easily validate this with PHP code like this:
$constraint = new Assert\Collection(array(
"fields" => array(
"format" => new Assert\Collection(array(
"fields" => array(
"type" => new Assert\Choice(["foo", "bar"])
)
))
)
));
$violations = $validator->validate($data, $constraint);
foreach ($violations as $v) {
echo $v->getMessage();
}
If type is neither foo, nor bar, then I get a violation. Even if type is something exotic like a DateTime object, I still get a violation. Easy!
But if I set my data to this:
{
"format": "uh oh"
}
Then instead of getting a violation (because Assert\Collection expects an array), I get a nasty PHP message:
Fatal error: Uncaught Symfony\Component\Validator\Exception\UnexpectedTypeException: Expected argument of type "array or Traversable and ArrayAccess", "string" given [..]
If there a neat way to handle things like this, without needing to try / catch and handle the error manually, and without having to double up on validation (e.g. one validation to check if format is an array, then another validation to check if type is valid)?
Gist with the full code is here: https://gist.github.com/Grayda/fec0ed7487641645304dee668f2163ac
I'm using Symfony 4
As far as I can see, all built-in validators throw an exception when they are expecting an array but receive something else, so you'll have to write your own validator. You can create a custom validator that first checks if the field is an array, and only then runs the rest of the validators.
The constraint:
namespace App\Validation;
use Symfony\Component\Validator\Constraints\Composite;
/**
* #Annotation
* #Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
class IfArray extends Composite
{
public $message = 'This field should be an array.';
public $constraints = array();
public function getDefaultOption()
{
return 'constraints';
}
public function getRequiredOptions()
{
return array('constraints');
}
protected function getCompositeOption()
{
return 'constraints';
}
}
And the validator:
namespace App\Validation;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class IfArrayValidator extends ConstraintValidator
{
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof IfArray) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\IfArray');
}
if (null === $value) {
return;
}
if (!is_array($value) && !$value instanceof \Traversable) {
$this->context->buildViolation($constraint->message)
->addViolation();
return;
}
$context = $this->context;
$validator = $context->getValidator()->inContext($context);
$validator->validate($value, $constraint->constraints);
}
}
Note that this is very similar to the All constraint, with the major difference being that if !is_array($value) && !$value instanceof \Traversable is true, the code will add a violation instead of throwing an exception.
The new constraint can now be used like this:
$constraint = new Assert\Collection(array(
"fields" => array(
"format" => new IfArray(array(
"constraints" => new Assert\Collection(array(
"fields" => array(
"type" => new Assert\Choice(["foo", "bar"])
)
))
)),
)
));

Exclude some fields in findAll Symfony2

I use this code for getting all users in the database
$users= $this->getDoctrine()
->getRepository('AppBundle:Users')
->findAll();
return $this->render('livre/users.html.twig',array(
'users' => $users,
));
But me I want get only some fields sush as name,email and hide fields like password..
Thanks.
You can do it by this way:
1/ Create a specific method in the UserRepository class:
public function findByNameAndEmail()
{
$query = $this->createQueryBuilder('u')
->select('u.name, u.email') // where name & email are properties of your User entity
;
return $query->getQuery()->getResult();
}
2/ And, call it in your controller:
public function someAction()
{
$users = $this->getDoctrine()->getRepository('AppBundle:Users')->findByNameAndEmail();
return $this->render('livre/users.html.twig',array(
'users' => $users,
));
}

One to many relation throw exception on save

I have two entites. "Institution" and "User". The Insitution have a onetomany relation to user. I create a form "InstitutionType" and if i want to save a new insitution the "handleReqeust($request)" throw this exception.
Neither the property "users" nor one of the methods "addUser()"/"removeUser()", "setUsers()", "users()", "__set()" or "__call()" exist and have public access in class "App\UserBundle\Entity\Institution".
Entity User
/**
* #ORM\ManyToOne(targetEntity="Institution", inversedBy="users")
* #ORM\JoinColumn(name="institution_id", referencedColumnName="id")
*/
protected $institution;
public function setInstitution(\App\UserBundle\Entity\Institution $institution = null)
{
$this->institution = $institution;
return $this;
}
public function getInstitution()
{
return $this->institution;
}
Entity Institution
/**
* #ORM\OneToMany(targetEntity="App\UserBundle\Entity\User", mappedBy="institution")
*/
protected $users;
public function addUser(\App\UserBundle\Entity\User $users)
{
$this->users[] = $users;
return $this;
}
public function removeUser(\Ebm\UserBundle\Entity\User $users)
{
$this->users->removeElement($users);
}
public function getUsers()
{
return $this->users;
}
InstitutionType
$users = $this->entityManager->getRepository('AppUserBundle:User')->findByIsActive(true);
->add('users', 'entity', array(
'label' => 'responsibleperson',
'attr' => array(),
'class' => 'AppUserBundle:User',
'choices' => $users,
'multiple' => false,
'expanded' => false,
'empty_value' => '----------------')
)
Can someone help my to solve this issue?
Rename addUser to addUsers and removeUser to removeUsers.
Symfony2/Doctrine obviously has no knowledge of singular/plural words and can't guess that to add a single entry to the users collection it would be semantically more correct to call addUser() instead of addUsers.
Please note that you can always use the console command doctrine:generate:entities AcmeDemoBundle:Institution to generate missing fields in the entity classes.
If this doesn't help you need to make sure you use the same notation in your form type like in your entity configuration (annotation, yml or xml).

Sorting Object Array in Symfony on the basis of date & time

//Suppose Entity Notes has property 'creationdate' & 'getCreationDate()' method to access.
DefaultController extends Controller {
public function indexAction(){
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository('Bundle:Notes');
$notes = $repository->findBy(array('userid' => $userId);
//Now I want to sort the notes array as per creation date using usort
usort($notes, array($this,"cmp"));
}
function cmp($a, $b) {
return strtotime($a->getCreationDate()) > strtotime($b->getCreationDate())? -1:1;
}
}
You can set the order in your call to the repository rather than after like so...
$notes = $repository->findBy(
array('userid' => $userId), // search criteria
array('creationdate' => 'ASC') // order criteria
);
I know you said you wanted to use usort but it seems kind of unnecessary.

Resources