I have a problem in my symfony security api in the service PasswordService. It can't find the encoder
I can't guess what should I do.
This is my security.yaaml file:
This is my code:
<?php
namespace App\Services;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class PasswordService
{
/**
* #var PasswordService
*/
private $userPasswordEncoder;
/**
* #param UserPasswordEncoderInterface $userPasswordEncoder
*/
public function __construct(UserPasswordEncoderInterface $userPasswordEncoder)
{
$this->userPasswordEncoder = $userPasswordEncoder;
}
/**
* #param object $entity
* #param string $password
* #return string
*/
public function encode(object $entity, string $password): string
{
return $this->userPasswordEncoder->encodePassword($entity, $password);
}
/**
* #param string $password
* #return int
*/
public function formatRequirement(string $password)
{
return preg_match('#^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W)#', $password);
}
/**
* #param object $entity
* #param string $password
* #return bool
*/
public function isValid(object $entity, string $password): bool
{
return $this->userPasswordEncoder->isPasswordValid($entity, $password);
}
}
There is no real error you just have a wrong annotation here:
/**
* #var PasswordService <========
*/
private $userPasswordEncoder;
If you remove it, it should be fine. (Or replace it with UserPasswordEncoderInterface)
As PHP 7.4 supports typed class properties: https://www.php.net/manual/en/migration74.new-features.php#migration74.new-features.core.typed-properties. Looks like a lot of code could be eliminated, in particular getters and setters that in entities and DTOs that was responsible for controlling properties types. For example such snippet:
class Foo implements BarInterface
{
/**
* #var int
*/
protected $id;
/**
* #var int|null
*/
protected $type;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
* #return $this
*/
public function setId(int $id)
{
$this->id = $id;
return $this;
}
/**
* #return int|null
*/
public function getType(): ?int
{
return $this->type;
}
/**
* #param int|null $type
* #return $this
*/
public function setType(?int $type)
{
$this->type = $type;
return $this;
}
}
Can be refactored to:
class Foo implements BarInterface
{
public int $id;
public ?int $type;
}
Am I right that this is good idea? What should I take into consideration while making such refactoring?
I created new entity in App\Entity\, and have this error:
Compile Error: Declaration of App\Entity\OrderItem::getVariant(): ?App\Entity\ProductVariantInterface must be compatible with Sylius\Component\Core\Model\OrderItem::getVariant(): ?Sylius\Component\Core\Model\ProductVariantInterface
This is my OrderItem.php:
<?php
namespace App\Entity;
use App\Entity\Constants\DeliveryLocationConstants;
use Sylius\Component\Core\Model\OrderItem as BaseOrderItem;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\OrderItemInterface as BaseOrderItemInterface;
/**
* #ORM\Entity()
* #ORM\Table(name="sylius_order_item")
*
* Class OrderItem
* #package App\Entity
*/
class OrderItem extends BaseOrderItem implements BaseOrderItemInterface
{
/** #var ProductVariantInterface */
protected $variant;
/** #var string */
protected $productName;
/** #var string */
protected $variantName;
/** #var string */
protected $optiune;
/**
* {#inheritdoc}
*/
public function getVariant(): ?ProductVariantInterface
{
return $this->variant;
}
/**
* {#inheritdoc}
*/
public function setVariant(?ProductVariantInterface $variant): void
{
$this->variant = $variant;
}
public function getProduct(): ?ProductInterface
{
return $this->variant->getProduct();
}
/**
* {#inheritdoc}
*/
public function getProductName(): ?string
{
return $this->productName ?: $this->variant->getProduct()->getName();
}
/**
* {#inheritdoc}
*/
public function getOptiune(): ?string
{
return $this->optiune;
}
/**
* {#inheritdoc}
*/
public function setOptiune(?string $optiune): void
{
$this->optiune = $optiune;
}
/**
* {#inheritdoc}
*/
public function setProductName(?string $productName): void
{
$this->productName = $productName;
}
/**
* {#inheritdoc}
*/
public function getVariantName(): ?string
{
return $this->variantName ?: $this->variant->getName();
}
/**
* {#inheritdoc}
*/
public function setVariantName(?string $variantName): void
{
$this->variantName = $variantName;
}
/**
* {#inheritdoc}
*/
public function equals(BaseOrderItemInterface $item): bool
{
return parent::equals($item) || ($item instanceof static && $item->getVariant() === $this->variant);
}
/**
* Returns sum of neutral and non neutral tax adjustments on order item and total tax of units.
*
* {#inheritdoc}
*/
public function getTaxTotal(): int
{
$taxTotal = 0;
foreach ($this->getAdjustments(AdjustmentInterface::TAX_ADJUSTMENT) as $taxAdjustment) {
$taxTotal += $taxAdjustment->getAmount();
}
foreach ($this->units as $unit) {
$taxTotal += $unit->getTaxTotal();
}
return $taxTotal;
}
/**
* Returns single unit price lowered by order unit promotions (each unit must have the same unit promotion discount)
*
* {#inheritdoc}
*/
public function getDiscountedUnitPrice(): int
{
if ($this->units->isEmpty()) {
return $this->unitPrice;
}
return
$this->unitPrice +
$this->units->first()->getAdjustmentsTotal(AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT)
;
}
/**
* {#inheritdoc}
*/
public function getSubtotal(): int
{
return $this->getDiscountedUnitPrice() * $this->quantity;
}
}
Error message is pretty clear. You must to redefine getVariant() method like this:
public function getVariant(): ?\Sylius\Component\Core\Model\ProductVariantInterface
{
return $this->variant;
}
because in PHP parent class BaseOrderItem persist you to return Core\Model\ProductVariantInterface in this method
I'm working on a symfony project to build a rest API, I have 4 entities related to each other like that :
I've installed FOSRestBundle to build just a GET web service, when i want to get a resource for example :
http://evaluation.dev/app_dev.php/api/families
I got this error :
message: "A circular reference has been detected (configured
limit:1).",
this is my controller :
<?php
namespace API\APIBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations as Rest;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Serializer;
class CartographyController extends Controller
{
/**
* #Rest\View()
* #Rest\Get("/posts")
* #param Request $request
* #return Response
*/
public function getPostsAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$posts = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Post')
->findAll();
return new Response($serializer->serialize($posts, 'json'));
}
/**
* #Rest\View()
* #Rest\Get("/employments")
* #param Request $request
* #return Response
*/
public function geEmploymentAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$employments = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Employment')
->findAll();
return new Response($serializer->serialize($employments, 'json'));
}
/**
* #Rest\View()
* #Rest\Get("/professions")
* #param Request $request
* #return Response
*/
public function geProfessionsAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$professions = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Profession')
->findAll();
return new Response($serializer->serialize($professions, 'json')); }
/**
* #Rest\View()
* #Rest\Get("/families")
* #param Request $request
* #return Response
*/
public function geFamiliesAction(Request $request)
{
$encoder = new JsonEncoder();
$normalizer = new GetSetMethodNormalizer();
$serializer = new Serializer(array($normalizer), array($encoder));
$families = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Family')
->findAll();
return new Response($serializer->serialize($families, 'json'));
}
}
Family entity :
<?php
namespace EvalBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* Family
*
* #ORM\Table(name="family")
* #ORM\Entity(repositoryClass="EvalBundle\Repository\FamilyRepository")
*/
class Family
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* One Family has Many Professions.
* #ORM\OneToMany(targetEntity="Profession", mappedBy="family",orphanRemoval=true,cascade={"persist", "remove"},fetch="EAGER")
*/
protected $professions;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Family
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getProfessions()
{
return $this->professions;
}
/**
* #param mixed $professions
*/
public function setProfessions($professions)
{
$this->professions = $professions;
}
public function __toString() {
return $this->name;
}
}
Profession entity :
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Profession
*
* #ORM\Table(name="profession")
* #ORM\Entity(repositoryClass="EvalBundle\Repository\ProfessionRepository")
*/
class Profession
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many professions have One Family.
* #ORM\ManyToOne(targetEntity="Family", inversedBy="professions",cascade={"persist", "remove"})
*/
public $family;
/**
* One Profession has Many Employment.
* #ORM\OneToMany(targetEntity="Employment", mappedBy="profession",cascade={"persist", "remove"}, orphanRemoval=true,fetch="EAGER")
*/
private $employments;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Profession
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getEmployments()
{
return $this->employments;
}
/**
* #param mixed $employments
*/
public function setEmployments($employments)
{
$this->employments = $employments;
}
/**
* #return mixed
*/
public function getFamily()
{
return $this->family;
}
/**
* #param mixed $family
*/
public function setFamily($family)
{
$this->family = $family;
}
public function __toString() {
return $this->name;
}
}
Post entity
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="EvalBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many Posts have One Employment.
* #ORM\ManyToOne(targetEntity="Employment", inversedBy="posts", fetch="EAGER")
* #ORM\JoinColumn(name="employment_id", referencedColumnName="id",nullable=false)
*/
public $employment;
/**
* One Post has Many LevelRequired.
* #ORM\OneToMany(targetEntity="RequiredLevel", mappedBy="post")
*/
private $requiredLevels;
/**
* One Post has Many PostEvaluation.
* #ORM\OneToMany(targetEntity="PostEvaluation", mappedBy="post")
*/
private $postEvaluations;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Post
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getEmployment()
{
return $this->employment;
}
public function getProfession(){
return $this->employment->profession;
}
public function getFamily(){
return $this->employment->profession->family;
}
/**
* #param mixed $employment
*/
public function setEmployment($employment)
{
$this->employment = $employment;
}
public function __toString() {
return $this->name;
}
}
employment entity
<?php
namespace EvalBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Employment
*
* #ORM\Table(name="employment")
* #ORM\Entity(repositoryClass="EvalBundle\Repository\EmploymentRepository")
*/
class Employment
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Many Employments have One Profession.
* #ORM\ManyToOne(targetEntity="Profession", inversedBy="employments",fetch="EAGER")
*/
public $profession;
/**
* One Employment has Many post.
* #ORM\OneToMany(targetEntity="Post", mappedBy="employment",cascade={"persist", "remove"}, orphanRemoval=true,cascade={"persist", "remove"})
*/
private $posts;
/**
* One Employment has One Grid.
* #ORM\OneToOne(targetEntity="Grid", mappedBy="employment")
*/
private $grid;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Employment
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getProfession()
{
return $this->profession;
}
/**
* #param mixed $profession
*/
public function setProfession($profession)
{
$this->profession = $profession;
}
/**
* #return mixed
*/
public function getPosts()
{
return $this->posts;
}
/**
* #param mixed $posts
*/
public function setPosts($posts)
{
$this->posts = $posts;
}
/**
* #return mixed
*/
public function getGrid()
{
return $this->grid;
}
/**
* #param mixed $grid
*/
public function setGrid($grid)
{
$this->grid = $grid;
}
public function __toString() {
return $this->name;
}
}
any solution please ?
For you example, you can avoid the CircularReference like this
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceLimit(1);
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array(new JsonEncoder()));
var_dump($serializer->serialize($org, 'json'));
But in your example, you don't use the FOSRestBundle for the view in your controller. The FOSRestController give you a handleView() and a view() method. Like this
class CartographyController extends FOSRestController
{
public function getPostsAction(Request $request)
{
$posts = $this->get('doctrine.orm.entity_manager')
->getRepository('EvalBundle:Post')
->findAll();
$view = $this->view($posts, 200);
return $this->handleView($view);
}
In this case, the serialiser is a service, this service is activated in config.yml :
In your app/config/config.yml
framework:
serializer: { enabled: true }
In order to avoid circularReference, you can do this.
In your app/config/services.yml
circular_reference_handler:
public: false
class: callback
factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
arguments: ["#serializer.mapping.class_metadata_factory", null, "#serializer.property_accessor"]
public: false
tags: [serializer.normalizer]
calls:
- method: setCircularReferenceLimit
arguments: [1]
- method: setCircularReferenceHandler
arguments: ["#circular_reference_handler"]
The factory can be like this:
namespace AppBundle\Serializer;
class CircularHandlerFactory
{
/**
* #return \Closure
*/
public static function getId()
{
return function ($object) {
return $object->getId();
};
}
}
Another idea is to use the GROUPS for the serializer. There is an annotation given by FosRestBundle :
#Rest\View(serializerGroups={"user"})
More information here:
https://symfony.com/doc/current/serializer.html#using-serialization-groups-annotations
Symfony2, FOSRestBundle. How to use group with JMSSerializerBundle?
I want to make a simple file uploader with Symfony 2 and Doctrine 2.
I've follow this tutorial :
http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html
and this one :
http://leny-bernard.com/fr/afficher/article/creer-un-site-facilement-en-symfony2-partie-4
Here is my Entity class :
namespace Luna\KBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Luna\KBundle\Entity\Media
*
* #ORM\Entity
*/
class Media
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $title
*/
private $title;
/**
* #var text $description
*/
private $description;
/**
* #var string $author
*/
private $author;
/**
* #var string $source
*/
private $source;
/**
* #Assert\File(maxSize="6000000")
*/
private $paths;
/**
* #var string $type
*/
private $type;
/**
* #var Luna\KBundle\Entity\object
*/
private $idobject;
/***********************************METHODS***********************************/
/**
* Set idobject
*
* #param Luna\KBundle\Entity\Object $idobject
*/
public function setIdobject(\Luna\KBundle\Entity\object $idobject)
{
$this->idObject = $idObject;
}
/**
* Get idObject
*
* #return Luna\KBundle\Entity\Object
*/
public function getIdObject()
{
return $this->idObject;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set description
*
* #param text $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Get description
*
* #return text
*/
public function getDescription()
{
return $this->description;
}
/**
* Set author
*
* #param string $author
*/
public function setAuthor($author)
{
$this->author = $author;
}
/**
* Get author
*
* #return string
*/
public function getAuthor()
{
return $this->author;
}
/**
* Set source
*
* #param string $source
*/
public function setSource($source)
{
$this->source = $source;
}
/**
* Get source
*
* #return string
*/
public function getSource()
{
return $this->source;
}
/**
* Set paths
*
* #param string $paths
*/
public function setPaths($paths)
{
$this->paths = $paths;
}
/**
* Get paths
*
* #return string
*/
public function getPaths()
{
return $this->paths;
}
/**
* Set type
*
* #param string $type
*/
public function setType($type)
{
$this->type = $type;
}
/**
* Get type
*
* #return string
*/
public function getType()
{
return $this->type;
}
public function getAbsolutePath()
{
return null === $this->paths ? null : $this->getUploadRootDir().'/'.$this->paths;
}
public function getWebPath()
{
return null === $this->paths ? null : $this->getUploadDir().'/'.$this->paths;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/mediaobject';
}
}
The fact is that #Assert\File(maxSize="6000000") is not working : I don't have a file uploader but just a simple texte field ?!
How can I make this works correctly ?
Regards Guys :)
EDIT : Here my Form builder
namespace Luna\KBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class MediaInit extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('title')
->add('description')
->add('author')
->add('source')
->add('paths')
->add('type')
->add('idObject')
;
}
}
And Here my twig template :
{% extends '::layout.html.twig' %}
{####################################### MEDIA INIT###########################}
{% block content %}
<h1>Creer un Media</h1>
Entrez les informations de votre media ici
<form action="{{ path('media_init') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<p>
<button type="submit">Creer</button>
</p>
</form>
{% endblock %}
When you want to upload a file you need to declare a path field & a virtual file field. So, your class needs to look like :
class Media
{
private $path;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
}
And your form:
class MediaInit extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('file');
}
}
It seems that $builder variable in MediaInit class is not initialized properly (with "Media" type).