I'm trying to translate a custom constraint message:
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Translation\TranslatorInterface;
/**
* #Annotation
*/
class YoutubeLink extends Constraint
{
public $message = '"{{ string }}" is not a valid Youtube link';
/**
* #var TranslatorInterface
*/
private $translator;
public function __construct($translator)
{
$this->translator = $translator;
}
}
I'm injecting the translator:
services:
App\Validator\Constraints\YoutubeLink:
arguments: [ "#translator" ]
tags:
- { name: validator.constraint_validator, alias: validator.youtube_link }
But how to translate the message now with the argument?
Just put the translation key in the entity since $message is public:
/**
* #var string
*
* #ORM\Column(type="string")
* #AppAssert\YoutubeLink(
* message = "link.invalid_youtube"
* )
*/
protected $url;
Related
I have a big problem with Symfony, Doctrine and partially incorrect data.
First of all
i use uuids for primary keys
i use jwts for authentication
i use symfony 5.4.x and doctrine-bundle 2.7.x
i use class inheriance for getting my pk on each entity
i donĀ“t use at all relations, because i want to have some flexibility to slice components into their origin e.g. security component as a microservice (but i think this is not really neccesarry on my question)
Sample of JWT content
{
"iat": xxx,
"exp": xxx,
"roles": [
"MY_ROLE"
],
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"tenant": {
"id": "11111111-2222-3333-4444-555555555555",
"name": "MyTenant"
},
"user": {
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"username": "MyUser",
"firstname": "My",
"name": "User"
}
}
Sample of Entity: AbstractEntity.php
<?php
namespace App\Entity;
use JsonSerializable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
/**
* Class AbstractEntity
* #package App\Entity
*
* #ORM\MappedSuperclass()
*/
abstract class AbstractEntity implements EntityInterface, JsonSerializable
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* #return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* #return bool
*/
public function isNew(): bool
{
return !isset($this->id);
}
/**
* #return array
*/
public function jsonSerialize(): array
{
return [
'id' => $this->getId()->toRfc4122()
];
}
/**
* #return Uuid
*/
public function __toString(): string
{
return $this->getId()->toRfc4122();
}
}
Sample of Entity: Settings.php
<?php
namespace App\Entity\Configuration;
use DateTime;
use App\Entity\AbstractEntity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
/**
* #ORM\Entity(repositoryClass="App\Repository\Configuration\SettingsRepository")
* #ORM\Table(name="configuration_settings", indexes={#ORM\Index(columns={"tenant"})})
*/
class Settings extends AbstractEntity
{
/**
* #var Uuid $tenant
*
* #ORM\Column(type="uuid")
*/
private Uuid $tenant;
/**
* #var string|null $payload
*
* #ORM\Column(type="json", nullable=true)
*/
private ?string $payload = "";
/**
* #return Uuid
*/
public function getTenant(): Uuid
{
return $this->tenant;
}
/**
* #param Uuid $tenant
*/
public function setTenant(Uuid $tenant): void
{
$this->tenant = $tenant;
}
/**
* #return string|null
*/
public function getPayload(): ?string
{
return $this->payload;
}
/**
* #param string|null $payload
*/
public function setPayload(?string $payload): void
{
$this->payload = $payload;
}
}
Sample of Controller: SettingsController.php
<?php
namespace App\Controller\Configuration\API;
use App\Entity\Configuration\Settings;
use App\Entity\Security\Role;
use App\Repository\Configuration\SettingsRepository;
use App\Service\Security\UserService;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Uid\Uuid;
/**
* Settings API controller.
*
* #Route("/api/configuration/settings", name="api_configuration_settings_")
*/
class SettingsController extends AbstractController
{
/**
* #var SettingsRepository
*/
private SettingsRepository $repository;
/**
* SettingsService constructor.
* #param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Settings::class);
}
/**
* #Route("/obtain", name="obtain", methods={"GET"})
*/
public function obtain(UserService $userService, TokenStorageInterface $tokenStorageInterface, JWTTokenManagerInterface $jwtManager): JsonResponse
{
$userService->isGranted([
Role::ROLE_TENANT_USER
]);
$token = $jwtManager->decode($tokenStorageInterface->getToken());
$tenantAsRFC4122 = $token['tenant']['id']; // Here we have: 11111111-2222-3333-4444-555555555555
$tenant = Uuid::fromString($tenantAsRFC4122); // Here we have a class of Symfony\Component\Uid\UuidV6;
$settings = $this->repository->findOneBy([
'tenant' => $tenant
]);
return new JsonResponse(
$settings
);
}
}
If i now call the API-URL with the Authorization Header including the Bearer as JWT, i will get the Settings for my tenant 11111111-2222-3333-4444-555555555555. But this works not always ...
I can't describe it exactly, because I can't reproduce it, but on one of the systems with a lot of traffic it happens from time to time that despite the correct bearer, i.e. correct tenant, simply wrong settings for another tenant come back.
Is there a doctrine cache somewhere that might cache queries and assign them to the wrong repsonses at the end of the day?
SELECT payload FROM configuration_settings WHERE tenant = '0x1234567890123456789' // here doctrine automaticly strips it to hex
I am so perplexed, because the SQL behind it looks correct and right, here times as an example shortened.
Hope someone can help me.
Thanks,
S.
I try to make a custom deletion logic via a custom repository:
namespace AppBundle\Repository;
use AppBundle\Entity\ContactEmail;
class ContactEmailRepository extends \Doctrine\ORM\EntityRepository
{
/**
* Remove an email from the database
* #param String $email
*/
public function deleteEmail($email)
{
$em=$this->getEntityManager();
$queryBuilder = $em->createQueryBuilder();
$queryBuilder->delete('AppBundle::ContactEmail','c')
->where('c.email=:email')
->setParameter('email',$email);
$query=$queryBuilder->getQuery();
$p = $query->execute();
return $p;
}
}
And I test it via a functional test:
namespace Tests\AppBundle\Repository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use AppBundle\Entity\ContactEmail;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\ORM\Tools\SchemaTool;
Use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use AppBundle\DataFixtures\ContactEmailDataFixture;
class ContactEmailTest extends KernelTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $entityManager;
/**
* {#inheritDoc}
*/
protected function setUp()
{
$kernel = self::bootKernel();
$this->entityManager = $kernel->getContainer()
->get('doctrine')
->getManager();
//In case leftover entries exist
$schemaTool = new SchemaTool($this->entityManager);
$metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
// Drop and recreate tables for all entities
$schemaTool->dropSchema($metadata);
$schemaTool->createSchema($metadata);
}
/**
* Testing whether a preloaded email will get deleted
*/
public function testDeletion()
{
$fixture = new ContactEmailDataFixture();
$fixture->load($this->entityManager);
/**
* #var Appbundle\Repository\ContactEmailRepository
*/
$repository=$this->entityManager->getRepository(ContactEmail::class);
$emailToDelete='jdoe#example.com';
$repository->deleteEmail($emailToDelete);
$emailSearched=$repository->findOneBy(['email'=>$emailToDelete]);
$this->assertEmpty($emailSearched);
}
}
My entity is the following:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* ContactEmail
*
* #ORM\Table(name="contact_email")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ContactEmailRepository")
* #UniqueEntity("email")
*/
class ContactEmail
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var String
* #ORM\Column(name="email",type="string",unique=true)
* #Assert\Email( message = "The email '{{ value }}' is not a valid email.")
*/
private $email;
/**
* #ORM\Column(name="date", type="datetime")
*/
private $createdAt;
public function __construct()
{
$this->createdAt=new \DateTime("now", new \DateTimeZone("UTC"));
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param String $email
* #return ContactEmail
*/
public function setEmail($email){
$this->email=$email;
return $this;
}
/**
* #return String
*/
public function getEmail(){
return $this->email;
}
public function getCreatedAt()
{
return $this->createdAt;
}
}
And I use the following DataFixture:
namespace AppBundle\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use AppBundle\Entity\ContactEmail;
class ContactEmailDataFixture extends Fixture
{
public function load(ObjectManager $manager)
{
$emails=['jdoe#example.com','example#gmail.com','user1#example.com'];
foreach($emails as $email){
$emailEntityInstance=new ContactEmail();
$emailEntityInstance->setEmail($email);
$manager->persist($emailEntityInstance);
$manager->flush();
}
}
}
The problem is that I get the following error when I run the test:
Doctrine\ORM\Query\QueryException: [Semantical Error] line 0, col 7 near 'AppBundle::ContactEmail': Error: Class 'AppBundle\Entity\' is not defined.
So I want to know what I did wrong on the query syntax?
In a class that extends Doctrine\ORM\EntityRepository and that is referenced in the entity's #Entity docblock annotation's repositoryClass attribute, you can get a query builder like this:
$queryBuilder = $this->createQueryBuilder('c')
->where('c.email=:email')
->setParameter('email',$email);
I have a problem with VichUploaderBundle, I followed all the instructions but I dont know how my Controller should handle this after finished form becouse image failed to uploaded and database did not write.
my Entity class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #Vich\Uploadable
*/
class Product
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ..... other fields
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="product_image", fileNameProperty="imageName", size="imageSize")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="integer")
*
* #var integer
*/
private $imageSize;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* #return Product
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
var_dump($image);
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* #return File|null
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*
* #return Product
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* #return string|null
*/
public function getImageName()
{
return $this->imageName;
}
/**
* #param integer $imageSize
*
* #return Product
*/
public function setImageSize($imageSize)
{
$this->imagesize = $imageSize;
return $this;
}
/**
* #return integer|null
*/
public function getImageSize()
{
return $this->imageSize;
}
}
my FormType class:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class AvatarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageFile', FileType::class, array('label' => 'Brochure (PDF file)'))
;
}
public function getName()
{
return 'avatar_form';
}
}
config:
vich_uploader:
db_driver: orm # or mongodb or propel or phpcr
mappings:
product_image:
uri_prefix: /image/avatar
upload_destination: '%kernel.root_dir%/../web/image/avatar'
Twig template:
<div>
{{ form_start(form) }}
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form.imageFile) }}
<button type="submit" class="btn btn-default">Submit</button>
{{ form_end(form) }}
</div>
and finally Controller:
namespace AppBundle\Controller\User;
use AppBundle\Entity\Product;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use AppBundle\Controller\InitController;
use AppBundle\Form\AvatarType;
class UserController extends Controller implements InitController
{
/**
* #Route("/user/avatar", name="AvatarAction")
*/
public function AvatarAction(Request $request)
{
$form = $this->createForm('AppBundle\Form\AvatarType',null,array(
// To set the action use $this->generateUrl('route_identifier')
'action' => $this->generateUrl('AvatarAction'),
'method' => 'POST'
));
$form->handleRequest($request);
if ($request->isMethod('POST')) {
if($form->isValid()){
//$track = new Product();
//$em = $this->getDoctrine()->getManager();
//$em->persist($track);
//$em->flush();
}
return $this->render('default/avatar.html.twig',array(
'form' => $form->createView()
));
}
}
Thanks for all your advice!
Maybe this is not the solution, but your line :
->add('imageFile', FileType::class, array('label' => 'Brochure (PDF file)')):
Should be :
->add('imageFile', VichFileType::class, array('label' => 'Brochure (PDF file)'))
More info : https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/form/vich_file_type.md
What Symfony tells you as error ?
Have a nice day
I'm trying to add a field 'name' to my user and this is how I proceeded
namespace User\UserBundle\Controller;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
class DefaultController extends BaseController
{
public function indexAction()
{
$response = parent::registerAction();
// ... do custom stuff
return $response;
}
And this is My userType
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('nom')
->add('prenom')
;
}
When I try to add,
{{ form_widget(form.name) }}
I get this error
Method "nom" for object "Symfony\Component\Form\FormView" does not exist in FOSUserBundle:Registration:register.html.twig at line
This is my user class
namespace User\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255, nullable=true )
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="prenom", type="string", length=255, nullable=true )
*/
private $prenom;
/**
* Set nom
*
* #param string $nom
* #return User
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set prenom
*
* #param string $prenom
* #return User
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
return $this;
}
/**
* Get prenom
*
* #return string
*/
public function getPrenom()
{
return $this->prenom;
}
}
This is what I have under app/config
enter image description here
What should I do more? Any suggestions please? i'm newly starter with FOSUserBundle. THanks
It looks like you're trying to add the nom and prenom fields to the registration form. So you need to override the FOSUserBundle registration form. Follow the guide at Overriding Default FOSUserBundle Forms which shows how to override any form.
Maybe this link will help you http://symfony.com/doc/current/bundles/FOSUserBundle/overriding_forms.html
This is my config.yml
Do you write it all in config.yml ?
you need to write this code in config.yml:
fos_user:
db_driver: orm
firewall_name: main
user_class: User\UserBundle\Entity\User
registration:
form:
type: user_userbundle_user
and the remaining write in ./config/services.yml
services:
fos_user.doctrine_registry:
alias: doctrine
app.form.registration:
class: UserUserBundle\Form\UserType
tags:
- { name: form.type, alias: user_userbundle_user }
So I finally got it ! I added construct to my userType
public function __construct($class)
{
parent::__construct($class);
}
And this is my new services.yml
parameters:
user_user.registration.class: User\UserBundle\Entity\User
services:
user.form.user:
class: User\UserBundle\Form\UserType
arguments: [%user_user.registration.class%]
tags:
- { name: form.type, alias: user_userbundle_user }
And that's it I hope it helps someOne. Thanks everyone
I have some problems with custom validator constraints. I want to validate a field code in my entity TicketCode.
But the validator is not called when I use if(form->isValid), I don't understand why.
My custom Validator needs the entity Manager to check with the DataBase if this code corresponds, else the validator sends message error.
My Entity
<?php
namespace My\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use My\MyBundle\Validator as TicketAssert;
/**
* My\MyBundle\Entity\TicketCode
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\MyBundle\Entity\TicketCodeRepository")
*/
class TicketCode
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $code
*
* #ORM\Column(name="code", type="string", length=255)
* #TicketAssert:ValidCode(entity="TicketBundle:TicketCode", property="code")
*/
protected $code;
}
My Class Constraint
<?php
namespace My\MyBundle\Validator;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class ValidCode extends Constraint
{
public $message = 'Code Invalide';
public $entity;
public $property;
public function ValidatedBy()
{
return 'my_ticket_validator';
}
public function requiredOptions()
{
return array('entity', 'property');
}
public function targets()
{
return self::CLASS_CONSTRAINT;
}
}
My Validator
<?php
namespace My\MyBundle\Validator;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ValidCodeValidator extends ConstraintValidator
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function isValid($value, Constraint $constraint)
{
$todayInObject = new \DateTime(); //Renvoit la date du jour
var_dump($todayInObject);
$ticketCode = $this->entityManager->getRepository('MyMyBundle:TicketCode')->findOneBy(array('code' => $constraint->property));
if ($ticketCode != null) {
if ($ticketCode->nbMaxUse != 0 && ($todayInObject >= $ticketCode->getDateBegin && $todayInObject <= $ticketCode->getDateEnd())) {
return true;
}
else {
$this->setMessage($constraint->message);
return false;
}
}
else {
$this->setMessage($constraint->message);
return false;
}
return false;
}
}
Finally my service.yml in my bundle.
parameters:
my_ticket.validator.validcode.class: My\MyBundle\Validator\ValideCodeValidator
services:
my_ticket.validator.validcode:
class: %my_ticket.validator.validcode.class%
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: validator.constraint_validator, alias: my_ticket_validator }
If you have any idea why my validator is never called, explain to me thanks.
You have an upper V in your function name ValidatedBy(). It is supposed to be validatedBy().