Symfony validation callback - symfony

I'm trying to validate my entity via static callback.
I was able to make it work following the Symfony guide but something isn't clear to me.
public static function validate($object, ExecutionContextInterface $context, $payload)
{
// somehow you have an array of "fake names"
$fakeNames = array(/* ... */);
// check if the name is actually a fake name
if (in_array($object->getFirstName(), $fakeNames)) {
$context->buildViolation('This name sounds totally fake!')
->atPath('firstName')
->addViolation()
;
}
}
It works fine when I populate my $fakeNames array but what if I want to make it "dynamic"? Let's say I want to pick that array from the parameters or from the database or wherever.
How am I supposed to pass stuff (eg. the container or entityManager) to this class from the moment that the constructor doesn't work and it has to be necessarily static?
Of course my approach may be completely wrong but I'm just using the symfony example and few other similar issues found on the internet that I'm trying to adapt to my case.

You can create a Constraint and Validator and register it as service so you can inject entityManager or anything you need, you can read more here:
https://symfony.com/doc/2.8/validation/custom_constraint.html
or if you are on symfony 3.3 it is already a service and you can just typehint it in your constructor:
https://symfony.com/doc/current/validation/custom_constraint.html

This is the solution I was able to find in the end.
It works smoothly and I hope it may be useful for someone else.
I've set the constraint on my validation.yml
User\UserBundle\Entity\Group:
constraints:
- User\UserBundle\Validator\Constraints\Roles\RolesConstraint: ~
Here is my RolesConstraint class
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\Validator\Constraint;
class RolesConstraint extends Constraint
{
/** #var string $message */
public $message = 'The role "{{ role }}" is not recognised.';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
}
and here is my RolesConstraintValidator class
<?php
namespace User\UserBundle\Validator\Constraints\Roles;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class RolesConstraintValidator extends ConstraintValidator
{
/** #var ContainerInterface */
private $containerInterface;
/**
* #param ContainerInterface $containerInterface
*/
public function __construct(ContainerInterface $containerInterface)
{
$this->containerInterface = $containerInterface;
}
/**
* #param \User\UserBundle\Entity\Group $object
* #param Constraint $constraint
*/
public function validate($object, Constraint $constraint)
{
if (!in_array($object->getRole(), $this->containerInterface->getParameter('roles'))) {
$this->context
->buildViolation($constraint->message)
->setParameter('{{ role }}', $object->getRole())
->addViolation();
}
}
}
Essentially, I set up a constraint which, every time a new user user is registered along with the role, that role must be among those set in the parameters. If not, it builds a violation.

Related

FOSUserBundle not working Validation

I'm using symfony 3.3 and php 7.0 with the FOSUserBundle version 2.1.1 and I just realize that in the User Entity if you just add in the setters parameters the type hint like for example this... the validation will fail.
<?php
namespace AppBundle\Entity;
use ...;
/**
* #ORM\Entity
* #ORM\Table(name="`user`")
*/
class User extends BaseUser
{
/**
* #Assert\NotBlank()
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $firstName;
/**
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* #param string $firstName
*/
public function setFirstName(string $firstName) <- IF YOU ADD THIS STRING AS THE PARAMETER THE VALIDATION FAILS
{
$this->firstName = $firstName;
}
}
So finally the line should be like this next:
public function setFirstName($firstName)
If anyone knows how to add the typehint without giving problems to the validation will be nice the hear news.
This is how Symfony Validator works by default: it first sets the value (null for instance), then the validation is performed, not the other way. Because your method does not accept null values, only strings:
public function setFirstName(string $firstName)
Most probably, you are encountering error Exception: Argument 1 passed to setFirstName() must be of the type string, null given.
To overcome this you either have to set empty data for the corresponding field to '' or detach your entities from the form component. Or you can force method to accept null values:
// php 7
public function setFirstName(string $firstName = null)
{
$this->firstName = (string) $firstName;
}
// >= php7.1
public function setFirstName(?string $firstName)
I urge you to stop mixing entities with forms and validators. Your core domain should be free from such low level concerns (vide SRP from the SOLID). Also by the look of your setters I can tell you are moving towards the antipattern called Anemic Domain Model.
Apparently the FOSUserBundle does not allow the typehint at the moment if you want to use the validation functionality.
Your method setFirstName() in the User class must be compatible with FOSUserBundle/Model/UserInterface->setFirstName($username)
The interface has no typehint, so you can't add the typehint in your method...

Symfony Customrepository inject translator

I am not sure if this is even best practice or possible at all.
So I have a situation where I use DataTables and I need to change a boolean value to text in order to display true/false instead of numbers. But I also need to do that in different languages.
Since I need this in several places in the app i was thinking that I should make an app specific Repository class that extends EntityRepository and use it as extended class for the repositories I am building. For this i want to inject translator object in in order to translate some keys, but translation is never set:
CustomRepository class
class CustomRepository extends EntityRepository
{
/**
* #var Translator
*/
protected $translator;
/**
* #param Translator $translator
*/
public function setTranslator(Translator $translator)
{
$this->translator = $translator; //*******this one is not set...
}
/**
* Replace bool results into string values
*
* #param $aRes
* #param $sField
*
* #return mixed
*/
protected function _replaceBoolToStringResult(&$aRes, $sField)
{
if (1 == $aRes[$sField]) {
$aRes[$sField] = str_replace('1', $this->translator->trans('site.true'), $aRes[$sField]);
} else {
$aRes[$sField] = str_replace('0', $this->translator->trans('site.false'), $aRes[$sField]);
}
return $aRes;
}
}
services.yml
app.custom.repository:
class: App\CommonBundle\Repository\CustomRepository
#should i call here all the constructor vars from EntityRepository class as arguments?
calls:
- [setTranslator, ["#translator.default"]]
Repository with custom DQL
class SettingsRepository extends CustomRepository
{
public function findOverviewSettingsAsJson()
{
$aResult = $this->createQueryBuilder('s')
->select('s.identifier, s.type, s.isActive')
->getQuery()
->getScalarResult();
// ******** HERE I WANT TO USE _replaceBoolToStringResult
return json_encode($aResult);
}
}
I found this article by Matthias to be useful on this issue. (I know link only answers are frowned on...)
You must use the factory pattern when you use a repository as a service.
See possible duplicates :
Symfony 2: Creating a service from a Repository
How to inject a repository into a service in Symfony2?
Note : the syntax changed in latest SF version : http://symfony.com/doc/current/components/dependency_injection/factories.html
Edit :
You should use your repository as a service :
app.custom.repository:
class: App\CommonBundle\Repository\CustomRepository
factory: ["#doctrine.orm.entity_manager", getRepository]
arguments:
- App\CommonBundle\Entity\CustomEntity
calls:
- [setTranslator, ["#translator.default"]]
Then call this service as any other service in your code. For example from inside a controller :
$this->get('app.custom.repository')->...

How to call doctrine in an entity class using symfony

Am using symfony framework for my application. And to save records in database I want call the $this->getDoctrine()->getManager(); method in my entity class. But when I did that it gave me the error:
Call to undefined method getDoctrine(),
Can some one tell me what is the right way to do this.
My entity class is like:
namespace Acme\SuperbAppBundle\Entity;
use Symfony\Component\DependencyInjection\Container;
use Doctrine\ORM\Mapping as ORM;
class Users
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
/**
* #var string
*/
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
* #return Users
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
function __construct($firstName){
$this->setFirstName($firstName);
}
function save(){
$em = $this->getDoctrine()->getManager();
$em->persist($create);
$em->flush();
}
}
And my controller method is like:
public function test(){
$create = new Users('Rajat');
$create->save();
}
Your save method is attempting to call
$this->getDoctrine();
Whereby $this is the current Class, and any other Class it inherits. As it stands, your current Class, User, is standalone, and does not have a getDoctrine() method. If your Class were to extend the Controller Class, it would have access to that method:
class User extends Controller
I believe this simple fix will work, although it probably doesn't make real sense for it to extend Controller, as it is a User Entity, and unrelated to a Controller. A preferred, more advanced method, would be to inject the Doctrine service into the User class.
Ok, first of all Doctrine Entities :
Handle the entity generation and configuration
Declare the operations on the setters and getters.
If you wana save an object into your entity there it's your User, you have two way to store this user:
One:
You can use entity manager to store a user and the entity will help you to create the right object using the seters and getters:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use PATH\TO\Users;
class ExampleController extends Controller
{
public function examplefunction()
{
$em = $this->getDoctrine()->getManager();
$entity = new Users();
$entity->setFirstName('Rajat');
$em->persist($entity);
$em->flush();
}
}
The other way is to create this entry using QueryBuilder but it's a bad way in your case.
Oh, i forgot please delete the save method in your entity Doctrine manager allready implement it.
Your controller probably doesnt extends Symfony\Bundle\FrameworkBundle\Controller\Controller ...
You should have controller defined like this example:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
}
Entity class does not extends ContainerAware / Controller, so you can't call $this->getDoctrine()->getManager(). I don't think your Entity class should extend to a Controller. Because your entity class will become a controller instance just because you want to access the doctrine manager. That's a not good practice. What you can do is inject doctrine manager to your Entity class through services.
I wrote a blog few weeks ago regarding injecting services container and accessing through constructor. You can inject doctrine entity manager in the same way you inject services container. You can take a look at that if you like :- http://anjanasilva.com/blog/injecting-services-in-symfony-2/
Here's a nice question regarding injecting doctrine manager. Make sure you read the answer as well. :- Symfony 2 EntityManager injection in service
And another nice tutorial on injecting custom repository manager instead of injecting the whole entity manager. Which I believe even a good solution. :- http://php-and-symfony.matthiasnoback.nl/2014/05/inject-a-repository-instead-of-an-entity-manager/
Hope this helps to increase your understanding about Symfony 2.
Cheers!

Symfony2 - FOSUserBundle - FindUserBy<field_in_custom_UserBundle>

I´ve created my own user Bundle, extending FOSUserBundle.
In my UserBundle, I have a memberID field, with its setters and getters.
I can already find users by memberID using EntityManager, and then I could find the user through UserManager matching the username/email/... obtained with that EntityManager query, but...
Is there a way to findUserByMemberID using UserManager?
Thank you.
Thank you for your replies. It seems it´s easier than all this stuff.
OwnUserBundle:
/**
* #ORM\Column(type="string")
*
*/
protected $memberID;
public function getMemberID()
{
return $this->memberID;
}
public function setMemberID($memberID)
{
$this->memberID = $memberID;
}
You can query FOSUserBundle:
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('memberID' => '123'));
Then, using method findUserBy(array(*OwnUserBundle_field* => *search_parameter_value*)) you´ll get the user/s instance.
Every query that is "not standard" one has to be written into a repository class
You have also to relate this class with one that represent you data model.
Suppose that your entity is called User, you have to do something like this
/**
* VendorName\UserBundle\Entity\User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="VendorName\UserBundle\Repository\UserRepository")
*/
class User implements AdvancedUserInterface
{
[...]
This says that every "custom" query for that entity will be fit into that repository class.
Now you have to create the repository
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function findUserByMemberID($id)
{
/* your logic here */
}
[...]
and you can use that in the following way
$userRepo = $this->em->getRepository('VendorUserBundle:User');
$userRepo->findUserByMemberID();
You could extend the UserManager from the FOSUserBundle in your bundle and write your own method. And you could follow these instructions http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes

Validating a password in Symfony2

I'm trying to put together a change password feature in Symfony2. I have a "current password" field, a "new password" field and a "confirm new password" field, and the part I'm currently focusing on is validating the "current password" field.
(By the way, I realize now that things like FOSUserBundle exist that would take care of a lot of these things for me, but I already built my authentication system based on the official Symfony documentation, and I don't have time right now to redo all my authentication code.)
What I'm imagining/hoping I can do is create a validation callback that says something like this:
// Entity/User.php
public function currentPasswordIsValid(ExecutionContext $context)
{
$currentPassword = $whatever; // whatever the user submitted as their current password
$factory = $this->get('security.encoder_factory'); // Getting the factory this way doesn't work in this context.
$encoder = $factory->getEncoder($this);
$encryptedCurrentPassword = $encoder->encodePassword($this->getPassword(), $this->getSalt());
if ($encyptedCurrentPassword != $this->getPassword() {
$context->addViolation('Current password is not valid', array(), null);
}
}
As you can see in my comments, there are at least a couple reasons why the above code doesn't work. I would just post specific questions about those particular issues, but maybe I'm barking up the wrong tree altogether. That's why I'm asking the overall question.
So, how can I validate a user's password?
There's a built-in constraint for that since Symfony 2.1.
First, you should create a custom validation constraint. You can register the validator as a service and inject whatever you need in it.
Second, since you probably don't want to add a field for the current password to the User class just to stick the constraint to it, you could use what is called a form model. Essentially, you create a class in the Form\Model namespace that holds the current password field and a reference to the user object. You can stick your custom constraint to that password field then. Then you create your password change form type against this form model.
Here's an example of a constraint from one of my projects:
<?php
namespace Vendor\Bundle\AppBundle\Validator\Constraints\User;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class CurrentPassword extends Constraint
{
public $message = "Your current password is not valid";
/**
* #return string
*/
public function validatedBy()
{
return 'user.validator.current_password';
}
}
And its validator:
<?php
namespace Vendor\Bundle\AppBundle\Validator\Constraints\User;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use JMS\DiExtraBundle\Annotation\Validator;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\DiExtraBundle\Annotation\Inject;
/**
* #Validator("user.validator.current_password")
*/
class CurrentPasswordValidator extends ConstraintValidator
{
/**
* #var EncoderFactoryInterface
*/
private $encoderFactory;
/**
* #var SecurityContextInterface
*/
private $securityContext;
/**
* #InjectParams({
* "encoderFactory" = #Inject("security.encoder_factory"),
* "securityContext" = #Inject("security.context")
* })
*
* #param EncoderFactoryInterface $encoderFactory
* #param SecurityContextInterface $securityContext
*/
public function __construct(EncoderFactoryInterface $encoderFactory,
SecurityContextInterface $securityContext)
{
$this->encoderFactory = $encoderFactory;
$this->securityContext = $securityContext;
}
/**
* #param string $currentPassword
* #param Constraint $constraint
* #return boolean
*/
public function isValid($currentPassword, Constraint $constraint)
{
$currentUser = $this->securityContext->getToken()->getUser();
$encoder = $this->encoderFactory->getEncoder($currentUser);
$isValid = $encoder->isPasswordValid(
$currentUser->getPassword(), $currentPassword, null
);
if (!$isValid) {
$this->setMessage($constraint->message);
return false;
}
return true;
}
}
I use my Blofwish password encoder bundle, so I don't pass salt as the third argument to the $encoder->isPasswordValid() method, but I think you'll be able to adapt this example to your needs yourself.
Also, I'm using JMSDiExtraBundle to simplify development, but you can of course use the classical service container configuration way.
In Symfony 2.1 you can use the built-in validator:
http://symfony.com/doc/master/reference/constraints/UserPassword.html
So for instance in your form builder:
// declare
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
// mapped=>false (new in 2.1) is to let the builder know this is not an entity field
->add('currentpassword', 'password', array('label'=>'Current password', 'mapped' => false, 'constraints' => new UserPassword()))
Apparently there's a bug right now with that validator so might or might now work
https://github.com/symfony/symfony/issues/5460
FOSUserBundle uses a ModelManager class which is separate from the base Model. You can check their implementation.
I ended up cutting the Gordian knot. I bypassed all of Symfony's form stuff and did all the logic in the controller.

Resources