I have problem with authentication via jwt:
public function getUser($credentials, UserProviderInterface $userProvider)
{
try {
$credentials = str_replace('Bearer ', '', $credentials);
$jwt = (array) JWT::decode(
$credentials,
$this->params->get('jwt_secret'),
['HS256']
);
return $this->em->getRepository(User::class)
->findOneBy([
'username' => $jwt['user'],
]);
}catch (\Exception $exception) {
throw new AuthenticationException($exception->getMessage());
}
}
I have this function and while entering valid credentials im getting this error: „Credentials check failed as the callable passed to CustomCredentials did not return \u0022true\u0022.”
I thought maybe in return function getRepository wants my repository of user entity, I passed parameters: getRepository(UserRepository::class) but im getting different error: The class \u0027App\UserRepository\u0027 was not found in chain configured namespaces App\Entity
And right now im kinda stuck without any good ideas and i would be grateful for and ides/thoguhts/clues
Edit1.
I thought that maybe in getRepository() parameter should be passed as string but also no, im getting error of: Class \u0027User\u0027 does not exist
Edit2.
UserRepository.php:
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* #method User|null find($id, $lockMode = null, $lockVersion = null)
* #method User|null findOneBy(array $criteria, array $orderBy = null)
* #method User[] findAll()
* #method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
}
$user->setPassword($newHashedPassword);
$this->_em->persist($user);
$this->_em->flush();
}
// /**
// * #return User[] Returns an array of User objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?User
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
and User.php Entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ApiResource()
* #ORM\Entity
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
#[ApiResource]
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
*/
private $username;
/**
* #Assert\NotBlank()
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\Column(type="array")
*/
private $roles;
public function __construct(){
$this->roles = array('ROLE_USER');
}
public function getEmail(){
return $this->email;
}
public function setEmail($email){
$this->email = $email;
}
public function getUsername(){
return $this->username;
}
public function setUsername($username){
$this->username = $username;
}
public function getPlainPassword(){
return $this->plainPassword;
}
public function setPlainPassword($password){
$this->plainPassword = $password;
}
public function getPassword(){
return $this->password;
}
public function setPassword($password){
$this->password = $password;
}
public function getSalt(){
return null;
}
public function getRoles(){
return $this->roles;
}
public function eraseCredentials(){
}
}
You are missing a link to your Repository class.
Edit your #ORM\Entity annotation with:
use App\Repository\UserRepository;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=UserRepository::class)
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
#[ApiResource]
class User implements UserInterface
{
Related
I am trying to create this API endpoint that will accept JSON payload and will calculate quote based on provided factors and their ratings.
I have Entities that contain ratings about
"age"
"postcode"
"ABI code"
These AgeRating, PostcodeRating and AbiRating entities implement RatingFactorInterface to force implementation of getRatingFactor() method.
QuoteController seems to be violating "Single Responsibility" and "Open/Close" design principles as the factors like "age", "postcode" can change - extra factor can be added or one of the factors might not be used.
I was thinking maybe it would be possible for rating factors to be specified in the dependency injection container, but can't seem to find a good example how this would work especially with factors that depend on other services like AbiCodeRating which also depends on ABI code which is returned by using third party API which accepts car registration number.
How do I refactor the controller and services so I'm not violating Single Responsibility and Open / Close design principles?
POST JSON Payload example
{
"age": 20,
"postcode": "PE3 8AF",
"regNo": "PJ63 LXR"
}
QuoteController
<?php
namespace App\Controller;
use App\Repository\AbiCodeRatingRepository;
use App\Repository\AgeRatingRepository;
use App\Repository\PostcodeRatingRepository;
use App\Service\AbiCodeLookup;
use App\Service\QuoteCalculator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class QuoteController
* #package App\Controller
*/
class QuoteController extends AbstractController
{
/**
* #Route("/", name="quote")
*
* #param Request $request
* #param AbiCodeRatingRepository $abiCodeRatingRepository
* #param AgeRatingRepository $ageRatingRepository
* #param PostcodeRatingRepository $postcodeRatingRepository
* #return JsonResponse
*/
public function index(Request $request, AbiCodeRatingRepository $abiCodeRatingRepository, AgeRatingRepository $ageRatingRepository, PostcodeRatingRepository $postcodeRatingRepository)
{
try{
$request = $this->transformJsonBody($request);
/**
* Quoting engine could be used with a different set of rating factors!
* Meaning age, postcode and regNo maybe not be required, some other rating factors might be introduced
* How to make controller to accept rating factors dynamically?
*/
if (!$request || !$request->get('age') || !$request->request->get('postcode') || !$request->get('regNo')){
throw new \Exception();
}
/**
* call to a third party API to look up the vehicle registration number and return an ABI code
* this is only required if AbiRating is used with the quoting engine
*/
$abiCode = AbiCodeLookup::getAbiCode($request->get('regNo'));
/**
* $abiCode is only required if postcodeRating is used by quoting engine
*/
$ratingFactors[] = $abiCodeRatingRepository->findOneBy(["abiCode"=>$abiCode]);
$ratingFactors[] = $ageRatingRepository->findOneBy(["age"=>$request->get("age")]);
/**
* $area is only required if postcodeRating is used by quoting engine
*/
$area = substr($request->get("postcode"),0,3);
$ratingFactors[] = $postcodeRatingRepository->findByPostcodeArea($area);
$premiumTotal = QuoteCalculator::calculate($ratingFactors);
$data = [
'status' => 200,
'success' => "Quote created successfully",
'quote' => $premiumTotal
];
return new JsonResponse($data,200);
}catch (\Exception $e){
$data = [
'status' => 422,
'errors' => "Data is not valid",
];
return new JsonResponse($data,422);
}
}
/**
* #param Request $request
* #return Request
*/
protected function transformJsonBody(Request $request)
{
$data = json_decode($request->getContent(), true);
if ($data === null) {
return $request;
}
$request->request->replace($data);
return $request;
}
}
AbiCodeRating
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\AbiCodeRatingRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource(
* collectionOperations={"get","post"},
* itemOperations={"get"}
* )
* #ORM\Entity(repositoryClass=AbiCodeRatingRepository::class)
*/
class AbiCodeRating implements RatingFactorInterface
{
/**
* #ORM\Id
* #ORM\Column(type="string", length=10)
*/
private $abiCode;
/**
* #ORM\Column(type="decimal", precision=10, scale=2, nullable=true)
*/
private $ratingFactor;
public function getAbiCode(): ?string
{
return $this->abiCode;
}
public function setAbiCode(string $abiCode): self
{
$this->abiCode = $abiCode;
return $this;
}
public function getRatingFactor(): ?float
{
return $this->ratingFactor;
}
public function setRatingFactor(?float $ratingFactor): self
{
$this->ratingFactor = $ratingFactor;
return $this;
}
}
AgeRating
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\AgeRatingRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource(
* collectionOperations={"get","post"},
* itemOperations={"get"}
* )
* #ORM\Entity(repositoryClass=AgeRatingRepository::class)
*/
class AgeRating implements RatingFactorInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
private $age;
/**
* #ORM\Column(type="decimal", precision=10, scale=2, nullable=true)
*/
private $ratingFactor;
public function getAge(): ?int
{
return $this->age;
}
public function setAge(int $age): self
{
$this->age = $age;
return $this;
}
public function getRatingFactor(): ?float
{
return $this->ratingFactor;
}
public function setRatingFactor(?float $ratingFactor): self
{
$this->ratingFactor = $ratingFactor;
return $this;
}
}
PostcodeRating
<?php
namespace App\Entity;
use App\Repository\PostcodeRatingRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=PostcodeRatingRepository::class)
*/
class PostcodeRating implements RatingFactorInterface
{
/**
* #ORM\Id
* #ORM\Column(type="string", length=4)
*/
private $postcodeArea;
/**
* #ORM\Column(type="decimal", precision=10, scale=2, nullable=true)
*/
private $ratingFactor;
public function getPostcodeArea(): ?string
{
return $this->postcodeArea;
}
public function setPostcodeArea(string $postcodeArea): self
{
$this->postcodeArea = $postcodeArea;
return $this;
}
public function getRatingFactor(): ?float
{
return $this->ratingFactor;
}
public function setRatingFactor(?float $ratingFactor): self
{
$this->ratingFactor = $ratingFactor;
return $this;
}
}
RatingFactorInterface
<?php
namespace App\Entity;
interface RatingFactorInterface
{
/**
* #return float|null
*/
public function getRatingFactor(): ?float;
}
AbiCodeRatingRepository
<?php
namespace App\Repository;
use App\Entity\AbiCodeRating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* #method AbiCodeRating|null find($id, $lockMode = null, $lockVersion = null)
* #method AbiCodeRating|null findOneBy(array $criteria, array $orderBy = null)
* #method AbiCodeRating[] findAll()
* #method AbiCodeRating[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AbiCodeRatingRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AbiCodeRating::class);
}
}
AgeRatingRepository
<?php
namespace App\Repository;
use App\Entity\AgeRating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* #method AgeRating|null find($id, $lockMode = null, $lockVersion = null)
* #method AgeRating|null findOneBy(array $criteria, array $orderBy = null)
* #method AgeRating[] findAll()
* #method AgeRating[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AgeRatingRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AgeRating::class);
}
}
PostcodeRatingRepository
<?php
namespace App\Repository;
use App\Entity\PostcodeRating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* #method PostcodeRating|null find($id, $lockMode = null, $lockVersion = null)
* #method PostcodeRating|null findOneBy(array $criteria, array $orderBy = null)
* #method PostcodeRating[] findAll()
* #method PostcodeRating[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PostcodeRatingRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PostcodeRating::class);
}
/**
* #return PostcodeRating Returns PostcodeRating objects based on area
*/
public function findByPostcodeArea($area): ?PostcodeRating
{
return $this->findOneBy(["postcodeArea"=>$area]);
}
}
AbiCodeLookup
<?php
namespace App\Service;
class AbiCodeLookup
{
public static function getAbiCode(string $regNumber){
/**
* create a request to third party api which would return abi code
* How to configure this service to be used only with regNo factor
*/
return "22529902";
}
}
QuoteCalculator
<?php
namespace App\Service;
use App\Entity\RatingFactorInterface;
/**
* Class QuoteCalculator
*/
class QuoteCalculator
{
/**
* #param array $ratingFactors
* #return float
*/
public static function calculate(array $ratingFactors): float
{
$premiumTotal = 500;
foreach ($ratingFactors as $ratingFactor){
$premiumTotal = $premiumTotal * ($ratingFactor instanceof RatingFactorInterface ? $ratingFactor->getRatingFactor() : 1);
}
return $premiumTotal ;
}
}
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 need your help on my problem for saving my data in table.
I use fosUserBundle and have different user types (Admin, Pro, Client) that are classes extending a base User entity.
I also have a Language entity linked to the User with a one-to-many association.
To subscribe, a userClient needs to check by checkbox the language(s) that he speaks.
The form page is ok, it displays the UserClient form (+ the parent User form) with the list of languages as checkboxes.
The problem is the data saved in table.
I have the same values in the language column:
O:43:"Doctrine\Common\Collections\ArrayCollection":1:{s:53:"Doctrine\Common\Collections\ArrayCollectionelements";a:2:{i:0;O:32:"LanguagesBundle\Entity\Languages":10:{s:36:"LanguagesBundle\Entity\Languagesid";i:2;s:38:"LanguagesBundle\Entity\Languagesname";s:7:"English";s:44:"LanguagesBundle\Entity\Languagesshort_slug";s:2:"en";s:43:"LanguagesBundle\Entity\Languageslong_slug";s:5:"en_EN";s:43:"LanguagesBundle\Entity\LanguagesisDefault";b:0;s:43:"LanguagesBundle\Entity\LanguagesupdatedAt";O:8:"DateTime":3:{s:4:"date";s:26:"2018-04-20 22:46:48.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Europe/Paris";}s:43:"LanguagesBundle\Entity\LanguagesimageFile";N;s:39:"LanguagesBundle\Entity\Languagesimage";s:39:"Flag_of_United_Kingdom_-_Circle-512.png";s:43:"LanguagesBundle\Entity\LanguagesimageSize";i:37621;s:44:"LanguagesBundle\Entity\LanguagesuserClient";N;}i:1;O:32:"LanguagesBundle\Entity\Languages":10:{s:36:"LanguagesBundle\Entity\Languagesid";i:1;s:38:"LanguagesBundle\Entity\Languagesname";s:9:"Français";s:44:"LanguagesBundle\Entity\Languagesshort_slug";s:2:"fr";s:43:"LanguagesBundle\Entity\Languageslong_slug";s:5:"fr_FR";s:43:"LanguagesBundle\Entity\LanguagesisDefault";b:1;s:43:"LanguagesBundle\Entity\LanguagesupdatedAt";O:8:"DateTime":3:{s:4:"date";s:26:"2018-04-20 22:45:50.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:12:"Europe/Paris";}s:43:"LanguagesBundle\Entity\LanguagesimageFile";N;s:39:"LanguagesBundle\Entity\Languagesimage";s:31:"Flag_of_France_-_Circle-512.png";s:43:"LanguagesBundle\Entity\LanguagesimageSize";i:23352;s:44:"LanguagesBundle\Entity\LanguagesuserClient";N;}}}
Here's my code:
User.php
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use UserBundle\Entity\UserAdmin;
use UserBundle\Entity\UserProfessional;
use UserBundle\Entity\UserClient;
use UserBundle\Entity\Adress;
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"client" = "UserClient", "professional"="UserProfessional", "admin"="UserAdmin"})
*/
abstract class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $firstname;
//[...] getters and setters
Entity: UserClient.php
<?php
// src/UserBundle/Entity/UserGuest.php
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class UserClient extends User {
/**
* #ORM\Column(type="array")
* #ORM\OneToMany(targetEntity="LanguagesBundle\Entity\Languages", mappedBy="userClient")
*/
private $languages;
public function __construct() {
parent::__construct();
$this->languages = new ArrayCollection();
}
/**
* Set languages
*
* #param array $languages
*
* #return UserClient
*/
public function setLanguages($languages)
{
$this->languages = $languages;
}
/**
* Get languages
*
* #return array
*/
public function getLanguages()
{
return $this->languages;
}
/**
* Add language
*
* #param \LanguagesBundle\Entity\Languages $language
*
* #return UserClient
*/
public function addLanguage(LanguagesBundle\Entity\Languages $language)
{
$this->languages[] = $language;
return $this;
}
/**
* Remove language
*
* #param \LanguagesBundle\Entity\Languages $language
*/
public function removeLanguage(\LanguagesBundle\Entity\Languages $language)
{
$this->languages->removeElement($language);
}
}
Entity Languages.php
<?php
namespace LanguagesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
*
* #Vich\Uploadable
*/
class Languages
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(type="string", length=2, unique=true)
*/
private $short_slug;
/**
* #var string
*
* #ORM\Column(type="string", length=5, unique=true)
* #Assert\NotBlank()
*/
private $long_slug;
/**
* #var string
*
* #ORM\Column(type="boolean", nullable=true)
*/
private $isDefault;
/**
* #ORM\Column(type="datetime")
* #var \DateTime
*/
private $updatedAt;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Assert\File(
* maxSize="1M",
* mimeTypes={"image/png", "image/jpeg", "image/pjpeg"}
* )
* #Vich\UploadableField(mapping="language_images", fileNameProperty="image", size="imageSize")
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255, nullable = true)
* #var string
*/
private $image;
/**
* #ORM\Column(type="integer")
*
* #var integer
*/
private $imageSize;
/**
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\UserClient", inversedBy="languages")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $userClient;
/**
* 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
*/
public function setImageFile(?File $image = null): void
{
$this->imageFile = $image;
if (null !== $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();
}
}
public function getImageFile()
{
return $this->imageFile;
}
/**
* Constructor
*/
public function __construct()
{
$this->updatedAt = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Languages
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set shortSlug
*
* #param string $shortSlug
*
* #return Languages
*/
public function setShortSlug($shortSlug)
{
$this->short_slug = $shortSlug;
return $this;
}
/**
* Get shortSlug
*
* #return string
*/
public function getShortSlug()
{
return $this->short_slug;
}
/**
* Set longSlug
*
* #param string $longSlug
*
* #return Languages
*/
public function setLongSlug($longSlug)
{
$this->long_slug = $longSlug;
return $this;
}
/**
* Get longSlug
*
* #return string
*/
public function getLongSlug()
{
return $this->long_slug;
}
/**
* Set isDefault
*
* #param boolean $isDefault
*
* #return Languages
*/
public function setIsDefault($isDefault)
{
$this->isDefault = $isDefault;
return $this;
}
/**
* Get isDefault
*
* #return boolean
*/
public function getIsDefault()
{
return $this->isDefault;
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
*
* #return Languages
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set image
*
* #param string $image
*
* #return Languages
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set imageSize
*
* #param integer $imageSize
*
* #return Languages
*/
public function setImageSize($imageSize)
{
$this->imageSize = $imageSize;
return $this;
}
/**
* Get imageSize
*
* #return integer
*/
public function getImageSize()
{
return $this->imageSize;
}
/**
* Set userClient
*
* #param \UserBundle\Entity\UserClient $userClient
*
* #return Languages
*/
public function setUserClient(\UserBundle\Entity\UserClient $userClient = null)
{
$this->userClient = $userClient;
return $this;
}
/**
* Get userClient
*
* #return \UserBundle\Entity\UserClient
*/
public function getUserClient()
{
return $this->userClient;
}
/**
* {#inheritdoc}
*/
public function __toString()
{
return $this->getName() ?: '-';
}
}
My FormType: RegistrationTypeClient.php
<?php
// src/UserBundle/Form/UserContactFormType.php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use LanguagesBundle\Services\LanguagesService;
use LanguagesBundle\Entity\Languages ;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class RegistrationClientType extends AbstractType {
protected $service;
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder
->add('languages', EntityType::class, [
'class' => 'LanguagesBundle:Languages',
'choice_label' => 'name',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.name', 'ASC');
},
'label' => 'Langues',
'expanded' => true,
'multiple' => true,
])
;
}
public function getParent() {
// on inclut le formulaire de base d'inscription utilisateur
return 'UserBundle\Form\RegistrationType';
}
public function getBlockPrefix() {
return 'app_user_registration_client';
}
// For Symfony 2.x
public function getName() {
return $this->getBlockPrefix();
}
public function configureOptions(OptionsResolver $resolver) {
$resolver
->setDefaults(array(
'data_class' => \UserBundle\Entity\UserClient::class,
))
//->setRequired('service_languages');
;
}
}
And my Controller:
public function registerClientAction(Request $request) {
/** #var $formFactory FactoryInterface */
//$formFactory = $this->get('fos_user.registration_pro.form.factory');
/** #var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = new UserClient();
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $this->createForm(RegistrationClientType::class, $user);
$form->setData($user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_FAILURE, $event);
if (null !== $response = $event->getResponse()) {
return $response;
}
}
return $this->render('UserBundle\Registration\register_client.html.twig', array(
'form' => $form->createView(),
));
}
Here's a picture showing the data saved in the table
I need the languages to be stored in the database as an array for userClient.
Could you help me please.
Thank's for re-edit my question Philip-B-
Thank's for your reading also.
I have resolve my problem by adding a foreach to transform my object to array:
The code is in my controller:
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
foreach ($user->getLanguages() as $value) {
$langue[] = $value->getShortSlug();
}
$user->setLanguages($langue);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
I get an SQL syntax error:
An exception occurred while executing 'INSERT INTO group (name) VALUES (?)' with params [\"Entwickler\"]:\n\nSQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'group (name) VALUES ('Entwickler')' at line 1
while I try to persist an entity with a ManyToMany relation.
Scenario
I have an entity called folder and an entity called group. There is a ManyToMany relationship between these entities, because one group can have multiple folders, and one folder can have multiple groups.
There is already one folder with id 1. Now I want to create a group and want to add the folder with id 1 to this group.
Group 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\GroupRepository")
*/
class Group
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Folder", inversedBy="groups")
* #ORM\JoinTable(
* name="group_folder",
* joinColumns={
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="folder_id", referencedColumnName="id")
* }
* )
*/
private $folder;
public function __construct()
{
$this->folder = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|Folder[]
*/
public function getFolder(): Collection
{
return $this->folder;
}
public function addFolder(Folder $folder): self
{
if (!$this->folder->contains($folder)) {
$this->folder[] = $folder;
}
return $this;
}
public function removeFolder(Folder $folder): self
{
if ($this->folder->contains($folder)) {
$this->folder->removeElement($folder);
}
return $this;
}
}
Folder Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\FolderRepository")
*/
class Folder
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=45)
* #Assert\NotBlank()
*/
private $name;
/**
* #var Secret[]
* #ORM\OneToMany(targetEntity="App\Entity\Secret", mappedBy="folder")
*/
private $secrets;
/**
* #var Folder[]
*
* #ORM\OneToMany(targetEntity="Folder", mappedBy="parent")
*/
private $children;
/**
* #var Folder
*
* #ORM\ManyToOne(targetEntity="Folder", inversedBy="children")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
private $parent;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Group", mappedBy="folder")
*/
private $groups;
public function __construct()
{
$this->children = new ArrayCollection();
$this->secrets = new ArrayCollection();
$this->groups = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return Secret[]|ArrayCollection
*/
public function getSecrets()
{
return $this->secrets;
}
/**
* #return Folder[]|ArrayCollection
*/
public function getChildren()
{
return $this->children;
}
/**
* #param Folder $children
*/
public function addChildren($children)
{
$this->children[] = $children;
$children->setParent($this);
}
/**
* #return Folder
*/
public function getParent()
{
return $this->parent;
}
/**
* #param Folder $parent
*/
public function setParent($parent)
{
$this->parent = $parent;
}
/**
* #return Collection|Group[]
*/
public function getGroups(): Collection
{
return $this->groups;
}
public function addGroup(Group $group): self
{
if (!$this->groups->contains($group)) {
$this->groups[] = $group;
$group->addFolder($this);
}
return $this;
}
public function removeGroup(Group $group): self
{
if ($this->groups->contains($group)) {
$this->groups->removeElement($group);
$group->removeFolder($this);
}
return $this;
}
}
GroupType Form
<?php
namespace App\Form;
use App\Entity\Folder;
use App\Entity\Group;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GroupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('folder', EntityType::class, [
'class' => Folder::class,
'multiple' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Group::class,
]);
}
}
Controller Action which I send the POST Request to
/**
* #param Request $request
* #return View
*
* #Rest\Post("/api/groups", name="api_group_create")
*/
public function CreateGroupAction(Request $request)
{
$group = new Group();
$form = $this->createForm(GroupType::class, $group);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($group);
$em->flush();
return View::create($group, 201);
}
return View::create($form, 400);
}
The request body I send to /api/groups
{
"group": {
"name": "Entwickler",
"folder": [
1
]
}
}
I tried many diffenrent ways to solve that problem, but nothing works as i expected. That drives me nuts for hours now.
Thanks to Mathieu Dormeval, that was the solution.
I altered the group entity definition from:
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
*/
class Group
To:
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupRepository")
* #ORM\Table(name="groups")
*/
class Group
I created a new entity in an entity folder where I already have some other used entity. However this new entity is not detected by doctrine:mapping:info nor doctrine:schema:validate
It appears the file is simply not taken into account (if I write an error inside symfony is executed without issue).
I was thinking about a VM system issue but then I tried to create other new files (such as a new YML,a new symfony form) and it works...
I also cleared the cache:clear and doctrine:cache:all options
here is the class:
<?php
namespace NRtworks\BusinessDimensionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use JsonSerializable;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Table(name="test")
*/
class test implements JsonSerializable
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=50)
*/
protected $name;
/**
* #ORM\Column(type="string", length=100)
*/
protected $description;
/**
* Constructor
*/
public function __construct($id = NULL)
{
$this->id = $id;
$this->name = "Chart Of Accounts";
$this->description = "Default chart of accounts";
}
//this method returns an array with default values
public function getDefaultObject()
{
$result = Array();
$result['id'] = $this->id;
$result['name'] = $this->name;
$result['description'] = $this->code;
return $result;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
/**
* Set name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*/
public function getName()
{
return $this->name;
}
/**
* Set code
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Get description
*/
public function getDescription()
{
return $this->description;
}
public function jsonSerialize()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description
);
}
}
?>
where could this come from ?
You need to define #ORM\Entity annotation for your class:
/**
* #ORM\Entity
* #ORM\Table(name="test")
*/
class test implements JsonSerializable
{