Symfony Validation with Data-Transformers - forms

I have a form for a Product entity and I'm putting tags inside it.
I followed the documentation and used a data-transformer : the user enter a space-separated string which is exploded into a Tag collection.
I now want to validate the string that is transformed into the collection so it cannot use meta-characters.
I tried this, but it doesn't work : (cf. symfony2 entity validation regexp a-z A-Z 0-9)
//AppBundle\Entity\Product.php
/**
* #ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"})
* #Assert\Regex(
* pattern="/[\w\s]+/",
* match=true,
* message="Your property should match my damn regex !"
* )
* #ORM\JoinTable(
* name="contenus_tags",
* joinColumns={#ORM\JoinColumn(name="contenu_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
private $tags;
How can I make this ?

Add a validation in the tag class on its name, and a "valid" assertion on tags in your product class:
//AppBundle\Entity\Tag.php
/**
* #Assert\Regex(
* pattern="/^[a-Z0-9]+$/",
* match=true,
* message="Your property should match my damn regex !"
* )
*/
private $name;
//AppBundle\Entity\Product.php
/**
* #ORM\ManyToMany(targetEntity="Tag", cascade={"persist", "remove"})
* #Assert\Valid()
* #Assert\Regex(
* pattern="/[\w\s]+/",
* match=true,
* message="Your property should match my damn regex !"
* )
* #ORM\JoinTable(
* name="contenus_tags",
* joinColumns={#ORM\JoinColumn(name="contenu_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
private $tags;

Related

Doctrine Join with composite keys

Main entity Conferimenti.php
/**
* Conferimenti
*
* #ORM\Table(name="conferimenti")
* #ORM\Entity(repositoryClass="App\Repository\ConferimentiRepository")
*/
class Conferimenti
{
/**
* #var int
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="conferimenti_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Infocomune")
* #ORM\JoinColumn(name="idcomune", referencedColumnName="id")
*/
private $idcomune;
/**
* #ORM\ManyToOne(targetEntity="Inforifiuti")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="tiporifiuto", referencedColumnName="codicerifiuto"),
* #ORM\JoinColumn(name="idcomune", referencedColumnName="idcomune")
* })
*/
private $tiporifiuto;
Inforifiuti.php
/**
* Inforifiuti
*
* #ORM\Table(name="inforifiuti")
* #ORM\Entity
*/
class Inforifiuti
{
/**
* #var int
*
* #ORM\Column(name="codicerifiuto", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $codicerifiuto;
/**
* #var int
*
* #ORM\Column(name="idcomune", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $idcomune;
/**
* #var string
*
* #ORM\Column(name="descrizione", type="string", length=80, nullable=false)
*/
private $descrizione;
Infocomune.php
/**
* Infocomune
*
* #ORM\Table(name="infocomune")
* #ORM\Entity
*/
class Infocomune
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="infocomune_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nome", type="string", length=80, nullable=false)
*/
private $nome;
I am tryng to execute this query (working on Postgres) but can't get it working with doctrine ORM :(
SELECT r.descrizione, SUM(c.peso) as totale
INNER JOIN inforifiuti AS r ON r.codicerifiuto = c.tiporifiuto AND r.idcomune = c.idcomune
WHERE c.idcomune = :id_comune AND c.dataora >= '2019-01-01' AND c.dataora <= '2019-10-30'
GROUP BY c.tiporifiuto, r.descrizione
Querybuilder
$query = $qb
->select(array('r.descrizione','SUM(c.peso) as totale'))
->innerJoin('c.tiporifiuto','r', Expr\Join::WITH, $qb->expr()->andX(
$qb->expr()->eq('c.tiporifiuto', 'r.codicerifiuto'),
$qb->expr()->eq('c.idcomune', 'r.idcomune')
))
->where('c.idcomune = :id_comune')
->andWhere($qb->expr()->between('c.dataora', ':date_from', ':date_to'))
->setParameter('id_comune', $id_comune)
->setParameter('date_from', $date_from, \Doctrine\DBAL\Types\Type::DATETIME)
->setParameter('date_to', $date_to, \Doctrine\DBAL\Types\Type::DATETIME)
->groupBy('c.tiporifiuto')
->addGroupBy('r.descrizione')
->getQuery();
I get this error
A single-valued association path expression to an entity with a composite primary key is not supported. Explicitly name the components of the composite primary key in the query.
tryed to replace ->GroupBy with but same error
->add('groupBy', new Expr\GroupBy(['c.tiporifiuto', 'r.descrizione']))
UPDATE
Tryed using direct DQL but same identical error
$query = $this->getEntityManager()->createQuery(
'SELECT r.descrizione, SUM(c.peso) as totale
FROM App\Entity\conferimenti AS c
INNER JOIN App\Entity\inforifiuti AS r
WHERE c.idcomune = :id_comune AND c.dataora >= :date_from AND c.dataora <= :date_to
GROUP BY c.tiporifiuto, r.descrizione')
->setParameter('id_comune', $id_comune)
->setParameter('date_from', $date_from, \Doctrine\DBAL\Types\Type::DATETIME)
->setParameter('date_to', $date_to, \Doctrine\DBAL\Types\Type::DATETIME);
A single-valued association path expression to an entity with a composite primary key is not supported. Explicitly name the components of the composite primary key in the query.
try this:
$this->manager->createQuery('SELECT .........');
like this example :
$query = $entityManager->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', 19.99);
the official documentation: https://symfony.com/doc/3.4/doctrine.html

Specific form with two enities with Symfony

I have a Category entity, this entity allow me to create a tree of my data :
I would like to create a DiscountGrid entity. A DiscountGrid is composed by a textfield name, and an array containing the discount percentage by category ($discount[idCategory] = $percentage).
In fact, I would like a form like this :
The output would be $discount[ 1 ] = 25, $discount[ 2 ] = 30,.....
I have no idea how handle this behavior with Symfony framework. Here is the declaration of my entities
class Category{
/**
* #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)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255, nullable=true)
*/
private $slug;
/**
* #var int
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #var int
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #var int
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="root", referencedColumnName="id", onDelete="CASCADE")
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
class DiscountGrid{
/**
* #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)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255)
*/
private $slug;
/**
* #var array
*
* #ORM\Column(name="grid", type="array")
*/
private $grid;
First of all, what you are trying to do will be a nightmare if you keep storing DiscountGrid::$grid as an array. Not only this will make your form creation extremely hard, but what will happen if someday you have to add a Category? Will you drag all the indexes to match the new Categories list? This was not your question, and I may be downvoted for suggesting that, but I definitely recommend you build your model a bit cleaner, since having a clean model is mandatory to use FormTypes properly.
What I would suggest is the following model:
DiscountGrid::$grid (rename it to $discounts) is a ManyToOne of a new entity called Discount.
Discount has an attribute $category which is a OneToMany towards Category and an attribute $reduction which is a float.
Once you have this, create a DiscountFormType with a single field reduction which is a PercentType.
Then, create another form DiscountGridFormType with a single field discounts as a CollectionType. This CollectionType should have the option entry_type set as DiscountFormType.
Finally, when you create your form in Controller, bind it a DiscountGrid entity with some Discounts in your discounts attribute. You should see a series of text boxes with percents at the end. That's the list of discounts where you can change values of the reductions.
After that, some form theming will help you display the category name next to the textbox. But I guess you already have some way to go before being at this point.

Doctrine2 Inheritance Mapping - Wrong ID used

i'm havin a little problem at the moment.
There's an parent Entity Comment with CommentType and CommentId and some other fields.
Also there are some differently CommentEntites (like CommentText or CommentPhoto) which extends the parent Entity.
The expected behaviour should be, that Doctrine loads Data with ContentId from Entity CommentText or CommentImage (depending on CommentType).
But at the moment it's loaded with the ID from the Parent Entity.
The Parent Entity:
use Doctrine\ORM\Mapping as ORM;
/**
* Comment
*
* #ORM\Table(name="comments")
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="comment_type", type="string")
* #ORM\DiscriminatorMap( {"txt" = "CommentTxt", "img" = "CommentImg"} )
*/
abstract class Comment
{
/**
* #var integer
*
* #ORM\Column(name="content_id", type="integer", nullable=true)
*/
private $contentId;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
//and so on
One of the Children Entities:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* CommentTxt
*
* #ORM\Table(name="comment_text")
* #ORM\Entity
*/
class CommentText extends Comment
{
/**
* #var string
*
* #ORM\Column(name="content", type="text", length=65535, nullable=false)
*/
private $content;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
//and some more
How can i change this behaviour?
Greets

ZF2 Annotation Validators NotEmpty and Int not working?

I'm building a form in ZF2 from an entity and everything seems to work fine, except 2 of my validators are ignored for some reason. The Entity looks like this:
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"NotEmpty"}) // duplicate
* #Annotation\Options({"label":"Name:"})
*/
private $name;
/**
* #var integer $sort
*
* #ORM\Column(name="sort", type="integer")
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"Int"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Sort:"})
*/
private $sort;
Yet I can submit the form without any values. I can enter a string in the SORT input, I can leave both fields empty. Why is this not working, why is there no errormessage when I leave the fields empty?
okay, it's partially because I'm STUPID.
Problem 1: notempty not working is because I defined nullable as true. Seems to override the "notempty" validator. duh.
Problem 2: I still don't know WHY that works, but if I declare the column type as string (instead of integer), suddenly the int validation works.
So here's the right code:
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Name:"})
*/
private $name;
/**
* #var integer $sort
*
* #ORM\Column(name="sort", type="string")
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"Int"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Sort:"})
*/
private $sort;

Trouble with Doctrine2 joined tables

I am migrating from Doctrine 1.2 to Doctrine 2.1 and am having problems with what should be a simple join
This is my first table:
/**
* Model_WBS
*
* #Table(name="WBS")
* #Entity
*/
class Model_Wbs
/**
* #var integer $id
*
* #Column(name="id", type="integer", length=8)
* #Id
* #GeneratedValue(strategy="SEQUENCE")
* #SequenceGenerator(sequenceName="wbs_seq_seq", allocationSize=1, initialValue=1)
*/
public $id;
.
.
.
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #OneToMany(targetEntity="Model_WbsFund", mappedBy="id")
* #JoinColumn(name="id", referencedColumnName="wbs_id")
*/
public $wbsfunds;
And this is my second table
/**
* Model_WbsFund
*
* #Table(name="WBS_FUNDS")
* #Entity
*/
class Model_WbsFund
/**
* #var integer $id
*
* #Column(name="id", type="integer", length=8)
* #Id
* #GeneratedValue(strategy="SEQUENCE")
* #SequenceGenerator(sequenceName="wbs_funds_seq_seq", allocationSize=1, initialValue=1)
*/
public $id;
/**
* #var integer $wbs_id
*
* #Column(name="wbs_id", type="integer", length=8)
*/
public $wbs_id;
.
.
.
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ManyToOne(targetEntity="Model_Wbs", inversedBy="wbs_id")
* #JoinColumn(name="wbs_id", referencedColumnName="id")
*/
public $WBS;
And here is my simple test
$testEntity = Model_Wbs::find(7185);
foreach($testEntity->getwbsfunds() as $wbsfundobj){
print("<br/>");
print($wbsfundobj->FUND->getFundCode()." -- ".$wbsfundobj->WBS->getWbs());
}
So I would expect to see the fund code and the wbs from the wbsfunds table that are associated with the wbs I first searched. (I put in the ->WBS->getWbs() to see if I was actually getting just the funds related to wbs id 7185).
Instead, I get all wbs_funds records. I printed out the query statement doctrine generated and it's not correct
SELECT t0.id AS ID1, t0.wbs_id AS WBS_ID2, t0.fund_id AS FUND_ID3, t0.wbs_id AS WBS_ID4, t0.fund_id AS FUND_ID5 FROM WBS_FUNDS t0
The interesting thing is that the link between wbsfundobj->FUND->geFundCode() works great, as does the wbsfundobj->WBS->getWBS(), but the link from WBS to WBS_FUNDS seems wrong.
Typically, you call find() on the repository for a entity. Something like:
// $em is your Entity Manager instance
$testEntity = $em->getRepository('\Model_Wbs')->find(7185);
// or a shorcut
$testEntity = $em->find('\Model_Wbs', 7185);
How you get a reference to the EntityManager depends upon how you bootstrapped Doctrine. If you used the Bisna application resource plugin, then it would be something like (in controller):
$em = $this->getInvokeArg('bootstrap')->getResource('doctrine')->getEntityManager();
Typically, I put this into the controller's init() method or write an action helper that encapsulates the steps above so that I could call it using something like:
$em = $this->_helper->doctrine->getEntityManager();