Alberto rafael P. avatar Alberto rafael P. 6 years ago

Tengo muchas ganas de ver este capĂ­tulo!!!

1 | Reply |

Proximamente! Este curso esta siendo publicado diariamente

Saludos!

| Reply |
Lechu85 avatar Lechu85 3 years ago edited

Hello.
it wouldn't be more convenient and safer to use instead of:

I if (null === $value || '' === $value) { ... }

this code:

I if (!empty($value)) { ... }
? :)

| Reply |

Hey Leszek C.

That's a good question. In theory, your solution looks better but it works a bit different, for instance, it will allow arrays to be passed in. In this case, Ryan it's been very explicit with what exact values he want to skip

Cheers!

1 | Reply |
Christian H. avatar Christian H. 4 years ago

The validator is not API platform specific, right?

I have a strange problem: a validator that always shows a violation test-wise.

This is triggered, as expected, on an API call. The same via EasyAdmin.

But not when I create a new object and write it to the database using Doctrine Eventmanager. Then the validator is not called.

Is that the way it is supposed to be? Or do I have something configured wrong?

I use Symfony 5.2. and PHP 8.0.3

| Reply |

Hey Christian H.

Yes, the validator is its own component the Symfony Validator. ApiPlatform has it already integrated, the same thing with EasyAdmin but in your Symfony app, if you are not relying on a Symfony Form, then you have to manually use the validator service. It's quite simple to use, here you can read more about it https://symfony.com/doc/cur...

Feel free to ask us any doubts about it. Cheers!

| Reply |
Christian H. avatar Christian H. MolloKhan 4 years ago

OK. I got it! Thanks!

What's the best practice? Call manually every time before a "persist"?

Or can this be done automatically (e.g. via lifecycle callbacks)? But then a API call would be validated twice (by Doctrine and API Platform, right?).

| Reply |

If you have many places where you're creating those objects, it may makes sense to create a service class that you can use everywhere and to it the object's data, so it can instantiate the object, use the validator service, and all that stuff.

Cheers!

| Reply |

I am writing my first custom validator and have hit a block. What I am trying to accomplish is as follows. I have an entity that contains a list of "courses" that the student has signed up for and another entity that has the students "grades". The reason that they are in separate entities is that the student can attempt an examination more than once for a maximum of 3 times. I need to write a custom validator to and use it in the "grades" entity. the vlaidator should check if the student has signed up for the "course" and the maximum number of tries has not been exhausted that is it is < 3. Is there aq way to fetch the data from the entity in the validator?

| Reply |

Hey sridharpandu !

Hmm. So if you're inside a custom validator that is validating the Grade entity (and assuming you have added this new, custom validation constraint annotation above your Grade class), then you already have access to the Grade object. From there, I'm assuming that there is some method like $grade->getStudent() to get the Student object that this grade belongs to. And there must also be some sort of $grade->getCourse() so that you know what Course this grade is for.

If so, you could use that Student object and Course object to query the "list of courses" entity to see if you get any results. I would autowiring that entity's repository into the validator to do that. You could also query the "grades" table WHERE student = this student and course = this course to see how many results you get back.

Does that help? Or is there some unclear piece... or some details I'm missing?

Cheers!

1 | Reply |

Swagger throws an error when I autowire the entity class into the validator. The error is :

<blockquote>Cannot autowire service "App\Validator\gradeValidator"; argument "$course" of method "__construct()" has type "App\Validator\Courses" but this class is not found.</blockquote>

My validator is a s follows

`gradeValidator.php


namespace App\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class gradeValidator extends ConstraintValidator

private $courses;

public function __construct(Courses $course)
{
    $this->course = $course);
 }

public function validate(4value, Constraint $constraint)
 {
  ........................
 }

}`

Traced back all lessons in Symfonycasts only to realize that entity repository autowiring was not covered. Then got a few posts on StackOverflow that discussed this problem on an older symfony version. Based on these suggestion I included the following use statements

use Doctrine\ORM\ServiceEntityRepository;<br />use Doctrine\ORM\ManagerRegistry;

but the error persisted. So read through few more Stackoverflow posts and one suggested to include the following instead of the two statements above.
use Doctrine\ORM\EntityManagerInterface;

But including these statements did not help. Any suggestions on what should I include in the validator so that the entity gets autowired.

Thanks in advance.

| Reply |

Hi sridharpandu!

Hmm. Ok, a few things :). Entity repositories are normal services. So you can autowire them in the same way as any other service. Don't forget that you can run bin/console debug:autowiring --all to see a full list of classes and interfaces that can be used for autowiring.

If your validator itself, what you want to autowire isn't a Courses class but something called CoursesRepository or CourseRepository. If you used make:entity to originally generate your entity, then this should have been created for you automatically in the src/Repository directory. THAT is service class you want to autowire.

Once you have access to it, you can create custom methods that make custom queries. Here is a video all about the repository topic - https://symfonycasts.com/screencast/symfony-doctrine/more-queries - the only difference in that (other than we call our repository from a controller instead of from a validator service) is that we autowire EntityManagerInterface and then use its ->getRepository() method to get the repository. Both actually work - you could autowire EntityManagerInterface into your constructor or (do the easier route) where you just autowire the CourseRepository directly. We change to autowire the repository directly later - you can see that in the next video - https://symfonycasts.com/screencast/symfony-doctrine/query-builder#autowiring-the-repository-directly

Cheers!

1 | Reply |

Thanks a ton. I got a bit confused with your previous answer being a bit different from the tutorail 12 - Custom Repository class. So was trying to autowire the entity. I have now managed to autowire the repository successfully except that Symfony 5.2 expects a use statement,

use App\Repository\CoursesRepository;

which is not visible in your videos. Is this expected behaviour?

In the meantime trying my hand at writing custom queries.

EDIT 1


Is there a way to refer to the data being passed to the entity that has triggered this validator? Does the $value argument of the validate function contain the object/data? A dd doesn't do anything on the API Platform. I want to accomplish a equi join between two entities so thge correct record is retrieved from the Database. In the video lessons one side of the argument passed is always a literal.

EDIT 2


The use case I described in my post can be solved on the GUI by incorporating a drop down list of courses where the number of tries <3 (implemented on API platform using a filter) so that the user can only create grades where the number of attempts is less than there but then this would mean delegating functional responsibility to the UI which is something that I would like to avoid by retaining all functional checks in the API.

| Reply |

Hey sridharpandu!

> I have now managed to autowire the repository successfully except that Symfony 5.2 expects a use statement... which is not visible in your videos. Is this expected behaviour?

Yes, good catch! And sorry about this. When you allow a class name to be auto-completed in PhpStorm, it automatically adds that use statement to the top of the class. So it IS being added when I code, but you can't see it. This does confuse some people (and I totally understand why), but if I scrolled up on every "use" statement to show it, it would be super annoying. So there is no perfect answer to this :). Btw, you can always check the code blocks on the page and expand them to see the full code for a spot.

> Is there a way to refer to the data being passed to the entity that has triggered this validator?
> Does the $value argument of the validate function contain the object/data? A dd doesn't do anything on the API Platform.

I'm surprised a dd() doesn't trigger anything: that *should*. As long as you have the validation annotation above a property or class (more info about this below), that should trigger the validate() method to be called.

The answer to "what" the $value argument will be depends *where* you put the annotation. For example, in this chapter, I put the @IsValidOwner annotation above the owner property. And so, I am passed THAT value. In this case, it means I am passed a User object. If I put a custom validation annotation above a "string" property, then I would be passed that string. You can *also* put an annotation above an entire *entity* - above the class. And in that case, you are passed the entire *object*. For example, if you put your annotation above the Course class, then the $value would be the Course argument. We have an example of this type of validator here: https://symfonycasts.com/sc...

Cheers!

1 | Reply |

Using the annotation at the attribute level triggers the validation.

I wasn't very explicit when I said that a dd() doesnt trigger anything, I used dd() to dump the variables especially the $value but didn't see anything on the swagger UI nor in the symfony profiler. But will check again to see if its hidden.

Thanks a lot for the detailed explanation on the contents of $value , it solves a lot of functional issues that I would encounter if didn't contain the value of the property or entity.

| Reply |

Hey sridharpandu!

> I used dd() to dump the variables especially the $value but didn't see anything on the swagger UI nor in the symfony profiler

Hmm. If you use dd(), it should "kill" the AJAX call. So you would need to look at your browser's network tools to find that AJAX request and look at its response. If you switch that to dump() instead, then you should be able to use the web debug toolbar AJAX icon on the bottom of your screen to find that AJAX request, open it in the profiler, and see the dump (via the Debug section on the left).

Good lucks and cheers!

1 | Reply |

Finally managed to complete a custom validator.Since I had autowired a repository class had to first write a working findBy() function. The video tutorials used setParameter() as the query builder had a single parameter, I had two!, so had to work on setParameters(). It takes an array of parameters so had to cast the variables into an array before I could use it. The reason for using andWhere() instead of where() was very helpful otherwise would have spent some more time reading the doctrine docs. I noticed that access to the properties of the classes that are injected into the validator can be only obtained by using the get methods and not by reference. For example

$value_courseName = $value->Coursename //Not allowed`<br />$value_courseName = $value->getCoursename() //Allowed

This is a fabulous implementation of object orientation and helps preserve scope and unnecessary manipulation of properties when an object's properties are passed by reference. There were several Front End Development tools in the late nineties that suffered from this drawback.

And finally the repository's findBy() method returns an array of objects, Accessing the right propery requires traversing this array with an off-set and accessing the objects get method like this

$courseName[0]->getCourseName()

Hope this hellps others understand the way Symfonyt is wired. Thanks a lot for answering my queries.

| Reply |

Hey sridharpandu!

Nice work!!! And I *really* appreciate your sharing your solution for others - we LOVE that here :).

Cheers!

1 | Reply |

Custom validators as a concept are awesome. It eases enterpr application development. Enterprise applications are a few forms with a zillion validations and processes. As a developer we can write once and use it anywhh. Simple validations like age, Social Security Number on application forms are being written several times withinin the same application it has been frustrating to identify these in code. Now I guess this structured approach will keep it clean and easy to maintain.

| Reply |

Hey Sridhar,

Agree, custom validators give you a lot of power and flexibility, so you literally can write any crazy business logic you need.

Cheers!

| Reply |