Symfony2 custom validator annotation not working - symfony

I have a custom validator in src/VNN/PressboxBundle/Component/Validator/CurrentPasswordValidator.php:
<?php
namespace VNN\PressboxBundle\Component\Validator\Constraints;
use Symfony\Component\Validator\ConstraintValidator,
Symfony\Component\Validator\Constraint,
Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface,
Symfony\Component\Security\Core\SecurityContextInterface,
JMS\DiExtraBundle\Annotation\Validator,
JMS\DiExtraBundle\Annotation\InjectParams,
JMS\DiExtraBundle\Annotation\Inject;
/**
* #Validator("user.validator.current_password")
*/
class CurrentPasswordValidator extends ConstraintValidator
{
// ...
}
And then I have this at src/VNN/PressboxBundle/Component/Validator/Contraints/CurrentPassword.php:
<?php
namespace VNN\PressboxBundle\Component\Validator\Constraints;
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';
}
}
For some reason, when I try to add an annotation that uses this validator, I get an error. Here's my entity/annotation:
<?php
namespace VNN\PressboxBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinTable as JoinTable;
use Doctrine\ORM\Mapping\JoinColumn as JoinColumn;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MaxLength;
use Symfony\Component\Validator\Constraints\Email;
use VNN\PressboxBundle\Component\Validator\Constraints\CurrentPassword;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* VNN\PressboxBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements UserInterface, \Serializable
{
/**
* #var string $password
* #Assert\NotBlank()
* #CurrentPassword()
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
}
The error I'm getting is:
AnnotationException: [Semantical Error] The annotation
"#VNN\PressboxBundle\Component\Validator\Constraints\CurrentPassword"
in property VNN\PressboxBundle\Entity\User::$password does not exist,
or could not be auto-loaded.
What am I doing wrong?

Check that VNN\PressboxBundle\VNNPressboxBundle() is correctly registered with the kernel in AppKernel.php.
Update: If it is correctly registered, and src/ is registered as a fallback with the autoloader (assuming VNN/ is under src/), you may want to try and load the service yourself manually and check the stack trace if/when it fails:
$validator = $container->get('user.validator.current_password');
// Pretending this is a unit test:
// $this->assertInstanceOf('VNN\PressboxBundle\Component\Validator\Constraints\CurrentPasswordValidator', $validator);
Also, make sure you have cleared the cache (if not testing in dev environment).
Side note (but unrelated to your problem) - if you start to add more validators, it will be more useful to use this syntax:
<?php
namespace VNN\PressboxBundle\Entity;
use VNN\PressboxBundle\Component\Validator\Constraints as VNN;
class User implements UserInterface, \Serializable
{
/**
* #VNN\CurrentPassword
*/
private $password;
}

Related

Persist twice on faker with nexest version

I have a problem on fixtures with fzaninotto/faker on a symfony project, i have some table with relations ManyToOne who not complete automaticaly when I load fixtures since i have update bundles on composer.
There is an error and a message who tell me to add cascad persist but if i do that the entry is create twice. The is my code and my error message, sorry for my bad english...
Project entity:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Table(name="`user`");
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $lastname;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Role")
* #ORM\JoinColumn(nullable=false)
*/
private $role;
Role Entity:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, unique=true)
*/
private $name;
/**
* #ORM\Column(type="string", length=100, unique=true)
*/
private $code;
/**
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="role")
*/
private $users;
Fixtures file:
<?php
namespace App\DataFixtures;
use Faker\Factory;
use App\Entity\Tag;
use App\Entity\Role;
use App\Entity\User;
use App\Entity\Skill;
use App\Entity\Follow;
use App\Entity\Statut;
use App\Entity\Techno;
use App\Entity\Comment;
use App\Entity\Project;
use App\Entity\Request;
use App\DataFixtures\Provider;
use Faker\ORM\Doctrine\Populator;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$generator = Factory::create('fr_FR');
// Ajout provider custom Provider
$generator->addProvider(new Provider($generator));
$populator = new Populator($generator, $manager);
// REMPLIT LES TABLES SIMPLES
// table "role"
$populator->addEntity(
Role::class,
2,
[
'name' => function () use ($generator) {
return $generator->unique()->randomElement(['Administrateur', 'Utilisateur']);
}
]
);
// table "user"
$populator->addEntity(
User::class,
10,
[
'firstname' => function () use ($generator) {
return ($generator->firstName());
},
'lastname' => function () use ($generator) {
return ($generator->lastName());
},
]
);
And my error message when i load fixtures:
In ORMInvalidArgumentException.php line 105:
Multiple non-persisted new entities were found through the given association graph:
* A new entity was found through the relationship 'App\Entity\User#role' that was not configured to cascade persist operations
for entity: Administrateur. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or conf
igure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
* A new entity was found through the relationship 'App\Entity\User#role' that was not configured to cascade persist operations
for entity: Utilisateur. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configu
re cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
Same issue with 1.9 version, so just downgrade :
1/ Remove faker folder in vendor.
2/ Change composer.json from
"fzaninotto/faker": "^1.9"
to
"fzaninotto/faker": "1.8"
3/ Run composer update
--> Breath now...

How to use separated password entity to prevent user from updating password with old used

I want to create a custom authentication system by separating passwords from user entity . each user can have more than a password and the latest one is used , and when user try to updated his password i want to prevent him to use an old password , as like as it is described in the link bellow .
Please i need your help and thank you .
https://filebin.net/wa6jrfy0t0xcqru7/Screenshot_from_2019-04-14_00-46-20.jpg?t=n9vnajox
For me is not a custom authentication system, you just need to change the body of the method getPassword. you will find below an example.
password.php
<?php
namespace your\name\space;
use Doctrine\ORM\Mapping as ORM;
use DateTime;
class Password
{
/**
* #ORM\ManyToOne(targetEntity="your\name\space\User", inversedBy="passwords")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*
* #var User
*/
private $user;
/**
* #ORM\Column(name="inserted_at", type="datetime")
*
* #var DateTime
*/
private $insertedAt;
}
User.php
<?php
namespace your\name\space;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
/**
* #ORM\OneToMany(targetEntity="your\name\space\Password", mappedBy="user")
* #ORM\OrderBy(value={"insertedAt" = "DESC"})
*
* #var Collection
*/
private $passwords;
/**
* User constructor.
*/
public function __construct()
{
$this->passwords = new ArrayCollection();
}
/**
* {#inheritDoc}
*/
public function getPassword()
{
if ($this->passwords->isEmpty()) {
return null;
}
return $this->passwords->first();
}
}

SonataUserBundle oneToMany Mapping Error

I dont know what else to try, this seems like it should be so straight forward, but the Symfony profiler continues to show this error:
Here are my User and Venue classes:
User:
namespace Application\Sonata\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Venue", mappedBy="user")
*/
protected $venues;
public function __construct()
{
parent::__construct();
$this->venues = new ArrayCollection();
}
}
Venue:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Venue
*
* #ORM\Table()
* #ORM\Entity
*/
class Venue
{
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="venues")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
It seems to work fine in the application, I only get the error inside the Symfony Profiler. It keeps telling me venues doesnt exist on User but Im looking right at it! What am I not doing correctly here?

Symfony2 Relationships using Interfaces results in duplicated table

I'm trying to relate an entity in one bundle with another in another bundle to make the second one independent from the first one, and be able to reuse it.
I'm following this documentation and this StackOverflows answer.
In the reusable bundle I have a Folder, File a that belongs to the folder and and interface like this:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class Folder implements FolderInterface
{
// Has many files
}
namespace Acme\FolderBundle\Entity;
interface FolderInterface
{
// no methods here
}
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class File
{
// Belongs to one folder
}
And on the other bundle just one class:
namespace Acme\NewBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Acme\FolderBundle\Entity\Folder as BaseFolder;
use Acme\FolderBundle\Entity\FolderInterface;
/**
* #ORM\Entity
*/
class Folder extends BaseFolder implements FolderInterface
{
// Has many files
}
And the config.yml's ORM configuration:
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
resolve_target_entities:
Acme\FolderBundle\Entity\FolderInterface: Acme\NewBundle\Entity\Folder
If I try to update my database schema, I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'foldersDatabase.folder' already exists.
To get this working, I have to explicitly change one of the Folder's Entities table:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
* #ORM\Table(name="distributed_folder")
*/
class Folder implements FolderInterface
{
// Has many files
}
Then, everything works but I get stuck with a table in my database (distributed_folder) that is never used.
Thanks a lot in advance!!
EDIT:
Fixed the annotation in the FolderInterface
You can not make one entity extend another entity this way.
If you want to have an abstract class which contains the fields for two or more subclass entities, you should mark the abstract class as #ORM\MappedSuperclass , and make sure, it will not have the annotation #Entity. While on the subclasses , they each should have #Entity annotation , and #Table annotation with a unique name attribute.
Here is an example :
<?php
namespace Radsphere\MissionBundle\Model\Core\BaseAbstract;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*
* An abstract class implementation of mission
*/
abstract class AbstractMission implements MissionInterface, IntegratedPluginInterface
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=36, unique=true)
*/
protected $guid;
/**
* #ORM\Column(type="string", length=255)
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="MissionTask", cascade={"persist", "remove"})
* #ORM\JoinTable(name="mtm_mission_task",
* joinColumns={#ORM\JoinColumn(name="mission_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $tasks;
/**
* {#inheritDoc}
*/
public function addTask(MissionTaskInterface $missionTask)
{
$this->getTasks()->add($missionTask);
$missionTask->setMission($this);
}
/**
* {#inheritDoc}
*/
public function setTasks(Collection $tasks)
{
/** #var MissionTaskInterface $task */
foreach ($tasks as $task) {
$task->setMission($this);
}
$this->tasks = $tasks;
}
/**
* {#inheritDoc}
*/
public function getTasks()
{
$tasks = $this->tasks;
foreach ($tasks as $task) {
if ($task instanceof MissionTaskInterface) {
if (!$task->getIsEnabled()) {
/** #var $tasks Collection */
$tasks->removeElement($task);
}
}
}
return $tasks;
}
}
and the entity itself:
<?php
namespace Radsphere\MissionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Radsphere\MissionBundle\Model\Core\BaseAbstract\AbstractMission;
/**
* Mission entity
*
* #ORM\Table(name="mission_bundle_mission", indexes={#ORM\Index(name="guid_idx", columns={"guid"})})
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="MissionRepository")
*/
class Mission extends AbstractMission
{
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}

override symfony2 bundle controller actions

/src/Vendor/JobQueueBundle/Controller/DefaultController.php
namespace Vendor\JobQueueBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\JobQueueBundle\Controller\JobController;
/**
* #Route("/jobs")
*/
class DefaultController extends JobController
{
/**
* #Route("/index")
*/
public function indexAction()
{
die();
}
}
/app/config/routing.yml
vendor_api_job_queue:
resource: "#VendorJobQueueBundle/Controller/"
type: annotation
prefix: /
JMSJobQueueBundle:
resource: "#JMSJobQueueBundle/Controller/"
type: annotation
/src/Syntetik/API/JobQueueBundle/SyntetikAPIJobQueueBundle.php
namespace Vendor\JobQueueBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class VendorJobQueueBundle extends Bundle
{
public function getParent()
{
return 'JMSJobQueueBundle';
}
}
When I try to call /jobs/index I get:
Method "JMS\JobQueueBundle\Controller\JobController::indexAction" does not exist.
DefaultController is completely ignored and not sure why?
Thanks!
It's known and opened issue of JMSDiExtraBundle https://github.com/schmittjoh/JMSDiExtraBundle/issues/39 so problem is that DiExtarBundle doesn't lookup parent class for annotations if child class has not at least one JMS annotation, so proxy class isn't generate at the metadata cache (look app/cache/dev/jms_diextra/metadata/)
The quickest solution is leave at least on annotation:
Parent Controller >>
<?php
namespace Namespace\SiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\DiExtraBundle\Annotation as DI;
class IndexController extends Controller
{
/**
* #DI\Inject("doctrine.orm.entity_manager")
* #var \Doctrine\ORM\EntityManager $em
*/
protected $em;
/**
* #DI\Inject("namespace.search.manager")
* #var Namespace\SearchBundle\Services\SearchManager $searchManager
*/
protected $searchManager;
/**
* #DI\Inject("namespace.product.manager")
* #var Namespace\ProductBundle\Services\ProductManager $productManager
*/
protected $productManager;
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction() {
echo "parent!";
$defaultCategory = $this->searchManager->getDefaultCategory();
....
return $result;
}
}
Child controller >>
<?php
namespace OtherSpace\SiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\DiExtraBundle\Annotation as DI;
use Namespace\SiteBundle\Controller\IndexController as BaseIndexController;
class IndexController extends BaseIndexController
{
/**
* The temprary solution base on known JMS/DiExtraBundle open issue https://github.com/schmittjoh/JMSDiExtraBundle/issues/39
* **We need to leave at leat one Inject in child class to get a proxy generated**
*
* #DI\Inject("doctrine.orm.entity_manager")
* #var \Doctrine\ORM\EntityManager $em
*/
protected $em;
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction() {
echo "child!";
$result = parent::indexAction();
return $result;
}
}
So this way proxy class will be generated for SpaceOther-IndexController and annotations will work
I also worked around fix this issue, you can look to my pull request https://github.com/schmittjoh/JMSDiExtraBundle/pull/153
Figured out the problem with this. It seems it's not symfony2 specific.
The problem is JMS\DiExtraBundle\JMSDiExtraBundle which mess the things around. Just removing that bundle makes everything works by the book.
Thanks!

Resources