SF2 doesn't update my image - symfony

I've a problem with 'HasLifecycleCallbacks'. When I try to update only my image in my entity it's doesn't work (It doesn't display form error or php error). But if I update my image and a field (title or text) it's working !
There are no others bugs, all work except this feature.
namespace Acme\NewsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* News
*
* #ORM\Table(name="news")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="Acme\NewsBundle\Entity\NewsRepository")
*/
class News
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(length=255, unique=true)
*/
private $slug;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
private $oldFile;
/**
* #var string
*
* #ORM\Column(name="text", type="text")
*/
private $text;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
public function __construct()
{
$this->created = new \Datetime;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return News
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set slug
*
* #param string $slug
* #return News
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set image
*
* #param string $image
* #return News
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set file
*
* #param string $file
* #return News
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file
*
* #return string
*/
public function getFile()
{
return $this->file;
}
/**
* Set text
*
* #param string $text
* #return News
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* Get text
*
* #return string
*/
public function getText()
{
return $this->text;
}
/**
* Set created
*
* #param \DateTime $created
* #return News
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
// upload img
public function getFullFilePath()
{
return null === $this->file ? null : $this->getUploadRootDir(). $this->file;
}
public function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return $this->getTmpUploadRootDir()."news/";
}
protected function getTmpUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__ . '/../../../../web/up/';
}
public function getFilePath()
{
return null === $this->file ? null : "/web/up/news/". $this->file;
}
/**
* #ORM\PreUpdate()
*/
public function delOldFile()
{
if (null === $this->file) {
return;
}
if(file_exists($this->getUploadRootDir().$this->image)){
$oldDir = $this->getUploadRootDir().$this->image;
if(file_exists($oldDir)) {
unlink($oldDir);
}
}
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadFile()
{
var_dump(true); die();
// the file property can be empty if the field is not required
if (null === $this->file) {
return;
}
if(!is_dir($this->getUploadRootDir())){
mkdir($this->getUploadRootDir());
}
$extension = pathinfo($this->file->getClientOriginalName(), PATHINFO_EXTENSION);
$name = uniqid().'.'.$extension;
$this->file->move($this->getUploadRootDir().'/', $name);
// resize 145*145
$cropImg = $this->getUploadRootDir().'/'.$name;
$cropDest = $this->getUploadRootDir().'/'.$name;
$largeur = 145;
$hauteur = 145;
$dimension = getimagesize($cropImg);
$ratio = $dimension[0] / $dimension[1];
if($largeur == 0 && $hauteur == 0){ $largeur = $dimension[0]; $hauteur = $dimension[1]; }
else if($hauteur == 0){ $hauteur = round($largeur / $ratio); }
else if($largeur == 0){ $largeur = round($hauteur * $ratio); }
if($dimension[0] > ($largeur/$hauteur)*$dimension[1] ){ $dimY = $hauteur; $dimX = round($hauteur*$dimension[0]/$dimension[1]); $decalX=($dimX-$largeur)/2; $decalY=0;}
if($dimension[0] < ($largeur/$hauteur)*$dimension[1]){ $dimX = $largeur; $dimY = round($largeur*$dimension[1]/$dimension[0]); $decalY=($dimY-$hauteur)/2; $decalX=0;}
if($dimension[0] == ($largeur/$hauteur)*$dimension[1]){ $dimX = $largeur; $dimY = $hauteur; $decalX=0; $decalY=0;}
$miniature = imagecreatetruecolor ($largeur,$hauteur);
if(in_array($extension,array('jpeg','jpg','JPG','JPEG'))){$file = imagecreatefromjpeg($cropImg); }
elseif(in_array($extension,array('png','PNG'))){$file = imagecreatefrompng($cropImg); }
elseif(in_array($extension,array('gif','GIF'))){$file = imagecreatefromgif($cropImg); }
else{ return false; }
imagecopyresampled($miniature,$file,-$decalX,-$decalY,0,0,$dimX,$dimY,$dimension[0],$dimension[1]);
imagejpeg($miniature,$cropDest,90);
$this->setImage($name);
}
/**
* #ORM\PostPersist()
*/
public function moveFile()
{
if (null === $this->file) {
return;
}
if(!is_dir($this->getUploadRootDir())){
mkdir($this->getUploadRootDir());
}
}
/**
* #ORM\PreRemove()
*/
public function removeFile()
{
if ($this->getFullFilePath()) {
unlink($this->getFullFilePath());
}
}
}

This issue is a known bug, check out the doc: http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html#using-lifecycle-callbacks
To resolve this, the doc suggest you to use a "updatedAt" field that will be updated each time you persist your entity, even if only the file field changed.

Check that your entity form type has a mapped (mapped option set to true) image field.,
$builder->add('createdDateTime',null,
array("mapped" => true,
"required" => false,
"description" => "Thedatetime of creation")
);

Related

Update Action Returns Error

I created an entity that has an image/file field in it. The insert and the render of the records in this entity works perfectly, but the update function, to modify some or all the fields of the entity, return this error:
Error: Call to a member function move() on a non-object
So I suppose that the error is in the function moveImage() in entity itself, that is:
/**
* #ORM\PostPersist()
*/
public function moveImage()
{
if (null === $this->image)
{
return;
}
if (!is_dir($this->getUploadRootDir()))
{
mkdir($this->getUploadRootDir());
}
copy($this->getTmpUploadRootDir() . $this->image, $this->getFullImagePath());
unlink($this->getTmpUploadRootDir() . $this->image);
}
I don't know what is the problem, and if I'm guessing right. Any suggest?
This is the entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Ricette
*
* #ORM\Table()
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="AppBundle\Repository\RicetteRepository")
*/
class Ricette
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titolo", type="string", length=255)
*/
private $titolo;
/**
* #var string
*
* #ORM\Column(name="ingredienti", type="text")
*/
private $ingredienti;
/**
* #var string
*
* #ORM\Column(name="procedimento", type="text")
*/
private $procedimento;
/**
* #var integer
*
* #ORM\Column(name="quantita", type="integer")
*/
private $quantita;
/**
* #var integer
*
* #ORM\Column(name="difficolta", type="integer")
*/
private $difficolta;
/**
* #var string
*
* #ORM\Column(name="tempo", type="string", length=50)
*/
private $tempo;
/**
* #var string $image
* #Assert\File( maxSize = "60000000", mimeTypesMessage = "Perfavore inserisci un'immagine valida!")
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* #ORM\ManyToOne(targetEntity="Tipologia", inversedBy="ricette")
* #ORM\JoinColumn(name="tipologia_id", referencedColumnName="id")
*/
protected $tipologia;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set titolo
*
* #param string $titolo
*
* #return Ricette
*/
public function setTitolo($titolo)
{
$this->titolo = $titolo;
return $this;
}
/**
* Get titolo
*
* #return string
*/
public function getTitolo()
{
return $this->titolo;
}
/**
* Set ingredienti
*
* #param string $ingredienti
*
* #return Ricette
*/
public function setIngredienti($ingredienti)
{
$this->ingredienti = $ingredienti;
return $this;
}
/**
* Get ingredienti
*
* #return string
*/
public function getIngredienti()
{
return $this->ingredienti;
}
/**
* Set procedimento
*
* #param string $procedimento
*
* #return Ricette
*/
public function setProcedimento($procedimento)
{
$this->procedimento = $procedimento;
return $this;
}
/**
* Get procedimento
*
* #return string
*/
public function getProcedimento()
{
return $this->procedimento;
}
/**
* Set quantita
*
* #param integer $quantita
*
* #return Ricette
*/
public function setQuantita($quantita)
{
$this->quantita = $quantita;
return $this;
}
/**
* Get quantita
*
* #return integer
*/
public function getQuantita()
{
return $this->quantita;
}
/**
* Set difficolta
*
* #param integer $difficolta
*
* #return Ricette
*/
public function setDifficolta($difficolta)
{
$this->difficolta = $difficolta;
return $this;
}
/**
* Get difficolta
*
* #return int
*/
public function getDifficolta()
{
return $this->difficolta;
}
/**
* Set tempo
*
* #param string $tempo
*
* #return Ricette
*/
public function setTempo($tempo)
{
$this->tempo = $tempo;
return $this;
}
/**
* Get tempo
*
* #return string
*/
public function getTempo()
{
return $this->tempo;
}
/**
* Set image
*
* #param string $image
*/
public function setImage($image)
{
$this->image = $image;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
public function getFullImagePath()
{
return null === $this->image ? null : $this->getUploadRootDir() . $this->image;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return $this->getTmpUploadRootDir() . $this->getId() . "/";
}
protected function getTmpUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
return __DIR__ . '/../../../web/immaginiRicette/';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage()
{
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
/**
* #ORM\PostPersist()
*/
public function moveImage()
{
if (null === $this->image)
{
return;
}
if (!is_dir($this->getUploadRootDir()))
{
mkdir($this->getUploadRootDir());
}
copy($this->getTmpUploadRootDir() . $this->image, $this->getFullImagePath());
unlink($this->getTmpUploadRootDir() . $this->image);
}
/**
* #ORM\PreRemove()
*/
public function removeImage()
{
$file=$this->image;
if($file = $this->getFullImagePath() && file_exists($file)) {
unlink($file);
rmdir($this->getUploadRootDir());
}
}
/**
* Set tipologia
*
* #param \AppBundle\Entity\Tipologia $tipologia
*
* #return Ricette
*/
public function setTipologia(\AppBundle\Entity\Tipologia $tipologia = null)
{
$this->tipologia = $tipologia;
return $this;
}
/**
* Get tipologia
*
* #return \AppBundle\Entity\Tipologia
*/
public function getTipologia()
{
return $this->tipologia;
}
}

Symfony2 DQL on Fos Userbundle entity

I'm implementing a search on a website and for simplicity i've decided to create a class that does the hard work.
Basically my class takes the EntityManager and the Entities in which i want to search and creates the DQL and executes the query. For normal entities this works totally fine but when i pass the User entity (Extended from fos user bundle entity) i get this error :
[Semantical Error] line 0, col 139 near 'last_login LIKE': Error: Class Soundmerger\UserBundle\Entity\User has no field or association named last_login
The dql query generated seems fine and as it works for the other entities i doubt it comes form there.
Here is my Search class :
use Doctrine\Common\Util\Debug;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query;
class Search {
private $em;
private $searchables;
public function __construct(EntityManager $em, ArrayCollection $searchables){
$this->em = $em;
$this->searchables = $searchables;
}
public function search($query){
$words = explode("+", $query);
$qb = array();
$results = array();
foreach($this->searchables as $qNumber => $searchable){
$qb[$qNumber] = "SELECT s FROM $searchable s ";
$columns = $this->em->getClassMetadata($searchable)->getColumnNames();
foreach($words as $i => $word){
foreach($columns as $k => $column){
$string = "s.$column LIKE '%$word%'";
if($i == 0 && $k == 0){
$qb[$qNumber] .= "WHERE $string";
} else {
$qb[$qNumber] .= "OR $string";
}
}
}
$res[] = $this->em->createQuery($qb[$qNumber])->getResult(Query::HYDRATE_SIMPLEOBJECT);
}
return $this->formatSearch($res);
}
private function formatSearch($results){
$formatedResult = array();
foreach($results as $k => $result){
if(!empty($result)){
$formatedResult[$k]["type"] = get_class($result[0]);
$formatedResult[$k]["data"] = $result[0];
}
}
return $formatedResult;
}
}
And my Controller for the search :
/**
* #Route("/search/{query}")
* #Template()
*/
public function indexAction($query)
{
$em = $this->getDoctrine()->getManager();
$searchables = new ArrayCollection(
array(
"SoundmergerUserBundle:Professional",
"SoundmergerFaqBundle:Faq",
"SoundmergerUserBundle:User",
));
$search = new Search($em, $searchables);
return array('search' => $search->search($query));
}
And finally my user entity :
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
* #AttributeOverrides({
* #AttributeOverride(name="emailCanonical",
* column=#ORM\Column(
* name = "emailCanonical",
* type = "string",
* length = 255,
* nullable = true
* )
* ),
* #AttributeOverride(name="email",
* column=#ORM\Column(
* name = "email",
* type = "string",
* length = 255,
* nullable = true
* )
* ),
* #AttributeOverride(name="username",
* column=#ORM\Column(
* name = "username",
* type = "string",
* length = 255,
* unique = false
* )
* ),
* #AttributeOverride(name="usernameCanonical",
* column=#ORM\Column(
* name = "usernameCanonical",
* type = "string",
* length = 255,
* unique = false
* )
* ),
* })
*/
class User extends BaseUser
{
const STRUCTURE_SOLO = "solo";
const STRUCTURE_GROUP = "group";
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", nullable=true)
* #var string
*/
protected $nomProfile = null;
/**
* #ORM\Column(type="text", nullable=true)
* #var string
*/
protected $description = null;
/**
* #ORM\Column(type="string", nullable=true)
* #var string
*/
protected $type = null;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $structure = self::STRUCTURE_SOLO;
/**
* #ORM\Column(type="string", nullable=true)
* #var string
*/
protected $tel = null;
/**
* #ORM\Column(type="string", nullable=true)
* #var string
*/
protected $site = null;
/**
* #ORM\Column(type="boolean")
* #var bool
*/
protected $firstLogin = true;
/**
* #ORM\OneToOne(targetEntity="Soundmerger\MainBundle\Entity\Address", cascade={"persist"})
*/
protected $address;
/**
* #ORM\OneToMany(targetEntity="Soundmerger\MainBundle\Entity\Style", mappedBy="user", cascade={"persist"})
*/
protected $styles;
/**
* #ORM\OneToMany(targetEntity="Soundmerger\UserBundle\Entity\Member", mappedBy="user")
* #ORM\JoinColumn(nullable=true)
*/
protected $members;
/** #ORM\Column(name="discogs_id", type="string", length=255, nullable=true) */
protected $discogs_id;
/** #ORM\Column(name="discogs_access_token", type="string", length=255, nullable=true) */
protected $discogs_access_token;
public function __construct()
{
parent::__construct();
$this->members = new ArrayCollection();
$this->styles = new ArrayCollection();
}
/**
* Get id
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Get NomProfile
* #return string
*/
public function getNomProfile()
{
return $this->nomProfile;
}
/**
* Get type
* #return string
*/
public function getType()
{
return $this->type;
}
/**
* Get structure
* #return string
*/
public function getStructure()
{
return $this->structure;
}
/**
* Get tel
* #return string
*/
public function getTel()
{
return $this->tel;
}
/**
* Get site
* #return string
*/
public function getSite()
{
return $this->site;
}
/**
* Get description
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set nomProfile
* #param string $nomProfile
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setNomProfile($nomProfile)
{
$this->nomProfile = $nomProfile;
return $this;
}
/**
* Set type
* #param string $type
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Set structure
* #param string $structure
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setStructure($structure)
{
if (!in_array($structure, array(self::STRUCTURE_SOLO, self::STRUCTURE_GROUP))) {
throw new \InvalidArgumentException("Invalid structure");
}
$this->structure = $structure;
return $this;
}
/**
* Set tel
* #param string $tel
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setTel($tel)
{
$this->tel = $tel;
return $this;
}
/**
* Set site
* #param string $site
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setSite($site)
{
$this->site = $site;
return $this;
}
/**
* Set description
* #param type $description
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Set the first login
* #param bool $firstLogin
* #return \Soundmerger\UserBundle\Entity\Utilisateur
*/
public function setFirstLogin($firstLogin)
{
$this->firstLogin = $firstLogin;
return $this;
}
/**
* Is it the first login
* #return bool returns true if its the first login, false otherwise
*/
public function isFirstLogin()
{
return $this->firstLogin;
}
/**
* Set address
*
* #param Address $address
* #return $this
*/
public function setAddress(Address $address)
{
$this->address = $address;
return $this;
}
/**
* Get adress
*
* #return mixed
*/
public function getAddress()
{
return $this->address;
}
/**
* #return Style
*/
public function getStyles()
{
return $this->styles;
}
/**
* #param Style $style
* #return $this
*/
public function addStyle(Style $style)
{
if (!$this->styles->contains($style)) {
$style->setUser($this);
$this->styles->add($style);
}
return $this;
}
public function setStyles(PersistentCollection $styles)
{
foreach ($styles as $style) {
$this->addStyle($style);
}
}
/**
* #return ArrayCollection
*/
public function getMembers()
{
return $this->members;
}
/**
* #param Member $members
*/
public function addMember(Member $member)
{
if (!$this->members->contains($member)) {
$this->members->add($member);
}
return $this;
}
public function setDiscogsId($discogs_id)
{
$this->discogs_id = $discogs_id;
return $this;
}
public function getDiscogsId()
{
return $this->discogs_id;
}
public function setDiscogsAccessToken($discogs_access_token)
{
$this->discogs_access_token = $discogs_access_token;
return $this;
}
public function getDiscogsAccessToken()
{
return $this->discogs_access_token;
}
public function isRegisteredWithDiscogs(){
return $this->discogs_id != null;
}
}
Any ideas on why this is only happening with the user entity (it must be due to the inheritance but i can't seem to find a way round it)
btw : its a university project so the idea of using an external bundle for the search is not really possible.
Thanks for any help
Dan

Symfony 2 Form Validation: missing name of property in validation message

I don't understand why some constraints does not insert name of property in error message after validation. I have this entity class:
<?php
namespace AC\OperaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AC\UserBundle\Entity\Utente;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Episodio
* #package AC\OperaBundle\Entity
* #ORM\Entity(repositoryClass="AC\OperaBundle\Repository\EpisodioRepository")
* * #ORM\Table(
* name="ac_Episodio",
* uniqueConstraints={#ORM\UniqueConstraint(name="unique_idx", columns={"opera", "numero_episodio", "extra"})},
* indexes={ #ORM\Index(name="opera_idx", columns={"opera"}),
* #ORM\Index(name="numero_episodio_idx", columns={"numero_episodio"}),
* #ORM\Index(name="extra_idx", columns={"extra"}),
* #ORM\Index(name="attivo_idx", columns={"attivo"}) })
*/
class Episodio {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Opera
*
* #ORM\ManyToOne(targetEntity="AC\OperaBundle\Entity\Opera", inversedBy="episodi")
* #ORM\JoinColumn(name="opera", referencedColumnName="id", nullable=false)
*/
protected $opera;
/**
* #var string
*
* #ORM\Column(name="numero_episodio", type="string", length=5, nullable=false)
*/
protected $numero_episodio;
/**
* #var string
*
* #ORM\Column(name="extra", type="string", length=30, nullable=false)
*/
protected $extra;
/**
* #var string
*
* #ORM\Column(name="titolo_italiano", type="string", length=150, nullable=false)
* #Assert\NotBlank()
*/
protected $titolo_italiano;
/**
* #var string
*
* #ORM\Column(name="titolo_originale", type="string", length=150, nullable=false)
* #Assert\NotBlank()
*/
protected $titolo_originale;
/**
* #var \DateTime
*
* #ORM\Column(name="data_ita", type="date", nullable=true)
* #Assert\Date()
*/
protected $data_ita;
/**
* #var \DateTime
*
* #ORM\Column(name="data_jap", type="date", nullable=true)
* #Assert\Date()
*/
protected $data_jap;
/**
* #var int
*
* #ORM\Column(name="durata", type="smallint", nullable=false, options={"default" = 0})
*/
protected $durata;
/**
* #var string
*
* #ORM\Column(name="trama", type="text", nullable=false)
*/
protected $trama;
/**
* #var int
*
* #ORM\Column(name="ordine", type="smallint", nullable=false, options={"default" = 0})
*/
protected $ordine;
/**
* #var int
*
* #ORM\Column(name="promosso", type="integer", nullable=false, options={"default" = 0})
*/
protected $promosso;
/**
* #var int
*
* #ORM\Column(name="rimandato", type="integer", nullable=false, options={"default" = 0})
*/
protected $rimandato;
/**
* #var int
*
* #ORM\Column(name="bocciato", type="integer", nullable=false, options={"default" = 0})
*/
protected $bocciato;
/**
* #var boolean
*
* #ORM\Column(name="fansub", type="boolean", nullable=false)
*/
protected $fansub;
/**
* #var boolean
*
* #ORM\Column(name="attivo", type="boolean", nullable=false)
*/
protected $attivo;
/**
* #var \DateTime
*
* #ORM\Column(name="data_aggiornamento", type="date")
*/
protected $data_aggiornamento;
/**
* #var Utente
*
* #ORM\ManyToOne(targetEntity="AC\UserBundle\Entity\Utente")
* #ORM\JoinColumn(name="utente_aggiornamento", referencedColumnName="id", nullable=true)
*/
protected $utente_aggiornamento;
/**
* #param boolean $attivo
*/
public function setAttivo($attivo)
{
$this->attivo = $attivo;
}
/**
* #return boolean
*/
public function getAttivo()
{
return $this->attivo;
}
/**
* #param int $bocciato
*/
public function setBocciato($bocciato)
{
$this->bocciato = $bocciato;
}
/**
* #return int
*/
public function getBocciato()
{
return $this->bocciato;
}
/**
* #param \DateTime $data_aggiornamento
*/
public function setDataAggiornamento($data_aggiornamento)
{
$this->data_aggiornamento = $data_aggiornamento;
}
/**
* #return \DateTime
*/
public function getDataAggiornamento()
{
return $this->data_aggiornamento;
}
/**
* #param \DateTime $data_ita
*/
public function setDataIta($data_ita)
{
$this->data_ita = $data_ita;
}
/**
* #return \DateTime
*/
public function getDataIta()
{
return $this->data_ita;
}
/**
* #param \DateTime $data_jap
*/
public function setDataJap($data_jap)
{
$this->data_jap = $data_jap;
}
/**
* #return \DateTime
*/
public function getDataJap()
{
return $this->data_jap;
}
/**
* #param int $durata
*/
public function setDurata($durata)
{
$this->durata = $durata;
}
/**
* #return int
*/
public function getDurata()
{
return $this->durata;
}
/**
* #param string $extra
*/
public function setExtra($extra)
{
$this->extra = $extra;
}
/**
* #return string
*/
public function getExtra()
{
return $this->extra;
}
/**
* #param boolean $fansub
*/
public function setFansub($fansub)
{
$this->fansub = $fansub;
}
/**
* #return boolean
*/
public function getFansub()
{
return $this->fansub;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param string $numero_episodio
*/
public function setNumeroEpisodio($numero_episodio)
{
$this->numero_episodio = $numero_episodio;
}
/**
* #return string
*/
public function getNumeroEpisodio()
{
return $this->numero_episodio;
}
/**
* #param \AC\OperaBundle\Entity\Opera $opera
*/
public function setOpera($opera)
{
$this->opera = $opera;
}
/**
* #return \AC\OperaBundle\Entity\Opera
*/
public function getOpera()
{
return $this->opera;
}
/**
* #param int $ordine
*/
public function setOrdine($ordine)
{
$this->ordine = $ordine;
}
/**
* #return int
*/
public function getOrdine()
{
return $this->ordine;
}
/**
* #param int $promosso
*/
public function setPromosso($promosso)
{
$this->promosso = $promosso;
}
/**
* #return int
*/
public function getPromosso()
{
return $this->promosso;
}
/**
* #param int $rimandato
*/
public function setRimandato($rimandato)
{
$this->rimandato = $rimandato;
}
/**
* #return int
*/
public function getRimandato()
{
return $this->rimandato;
}
/**
* #param string $titolo_italiano
*/
public function setTitoloItaliano($titolo_italiano)
{
$this->titolo_italiano = $titolo_italiano;
}
/**
* #return string
*/
public function getTitoloItaliano()
{
return $this->titolo_italiano;
}
/**
* #param string $titolo_originale
*/
public function setTitoloOriginale($titolo_originale)
{
$this->titolo_originale = $titolo_originale;
}
/**
* #return string
*/
public function getTitoloOriginale()
{
return $this->titolo_originale;
}
/**
* #param string $trama
*/
public function setTrama($trama)
{
$this->trama = $trama;
}
/**
* #return string
*/
public function getTrama()
{
return $this->trama;
}
/**
* #param \AC\UserBundle\Entity\Utente $utente_aggiornamento
*/
public function setUtenteAggiornamento($utente_aggiornamento)
{
$this->utente_aggiornamento = $utente_aggiornamento;
}
/**
* #return \AC\UserBundle\Entity\Utente
*/
public function getUtenteAggiornamento()
{
return $this->utente_aggiornamento;
}
}
In the controller perform the classi call at the $form->isValid() method to check validation. If there are errors i call $form->getErrorsAsString() and this is the result:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
numeroEpisodio:
No errors
titoloItaliano:
No errors
titoloOriginale:
No errors
dataIta:
ERROR: This value is not valid.
dataJap:
ERROR: This value is not valid.
durata:
No errors
trama:
No errors
extra:
No errors
fansub:
No errors
The property that use #Assert\NotBlank() dose not put property name in error message! And for this reason i have the first two error line with e generic:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
I i don't know who is the property that failed the validation. I look in the source code of Symfony Component and i see that for not blank constraint:
/**
* #author Bernhard Schussek <bschussek#gmail.com>
*
* #api
*/
class NotBlankValidator extends ConstraintValidator
{
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof NotBlank) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\NotBlank');
}
if (false === $value || (empty($value) && '0' != $value)) {
$this->context->addViolation($constraint->message);
}
}
}
And for data constraint:
/**
* #author Bernhard Schussek <bschussek#gmail.com>
*
* #api
*/
class DateValidator extends ConstraintValidator
{
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof Date) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Date');
}
if (null === $value || '' === $value || $value instanceof \DateTime) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string) $value;
if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
}
}
}
The important difference is $this->context->addViolation($constraint->message); VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));
Why?
This is core part of controller
if ($request->getMethod() == 'POST') {
try {
$form->handleRequest($request);
if ($form->isValid()) {
/* #var $item Episodio */
$item = $form->getData();
$em->persist($item);
$em->flush();
$msg = new Message(true, Message::OK_MESSAGE);
} else {
$msg = new Message(false, Message::KO_MESSAGE);
$errors = Utility::getErrorMessages($form);
$msg->setData($errors);
}
} catch (\Exception $ex) {
$msg = new Message(false, $ex->getMessage());
}
return new Response($this->get('jms_serializer')->serialize($msg, 'json'));
}
This is utility class that fetch error from form
class Utility {
static function getErrorMessages(\Symfony\Component\Form\Form $form) {
$errors = array();
foreach ($form->getErrors() as $key => $error) {
$template = $error->getMessageTemplate();
$parameters = $error->getMessageParameters();
foreach($parameters as $var => $value){
$template = str_replace($var, $value, $template);
}
$errors[$key] = $template;
}
//if ($form->hasChildren()) {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$child->getName()] = Utility::getErrorMessages($child);
}
}
//}
return $errors;
}
}
Form Class
class EpisodioForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('numeroEpisodio', 'text');
$builder->add('titoloItaliano', 'text',array());
$builder->add('titoloOriginale', 'text');
$builder->add('dataIta', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('dataJap', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('durata', 'text');
$builder->add('trama', 'textarea');
$builder->add('extra', 'text');
$builder->add('fansub', 'checkbox');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName()
{
return "episodio_form";
}
}
The version of framework is Symfony 2.4
Why $this->context->addViolation($constraint->message); VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));?
This is because the date constraint validator shows the value which failed to validate in the error message, e.g.
45.04.2014 is not a valid date.
whereas the NotBlank constraint validator doesn't need to, since the value causing the error is always empty.
Putting the property name in the error message
This could be achieved:
by changing the annotations for the constraints in your entity to something like:
class Episodio {
/**
* #Assert\NotBlank(message="'titolo_italiano' should not be blank.")
*/
protected $titolo_italiano;
/**
* #Assert\NotBlank(message="'titolo_originale' should not be blank.")
*/
protected $titolo_originale;
}
I left out the column definitions and the other fields in the entity for the sake of readability.
OR by defining a custom validator which passes the name of the property to the error message:
The constraint:
class MyCustomNotBlank extends NotBlank
{
public $message = "'{{ propertyName }}' should not be blank.";
}
The validator:
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class MyCustomNotBlankValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!$value) {
$this->context->addViolation(
$constraint->message,
array('{{ propertyName}}' => $this->context->getPropertyPath())
);
}
}
}
This shows you how to define a custom validation constraint:
http://symfony.com/doc/current/cookbook/validation/custom_constraint.html
Obtaining a key-value array with property name and error message
Afaik, there is no function in Symfony2's form component which does that, so it has to be implemented.
See https://stackoverflow.com/a/13763053/277106 . You can add that function to a service for reusability. In your (AJAX) controller you can then do:
public function someAction(Request $request)
{
$errors = array();
$form = $this->createForm(new MyType);
if (!$form->bindRequest($request)->isValid()) {
$errors = $this->myFormService->getErrorMessages($form);
}
else {
// save the entity
}
return JsonResponse($errors);
}

FindByTags with Symfony2?

I grab this kind of array.
This is a var_dump of my $tags variable.
array(2) { [0]=> string(1) "3" [1]=> string(1) "4" }
My entity
<?php
namespace Lan\CrmBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
*/
class Link
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=false)
* #Assert\NotBlank()
*/
private $url;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $created_at;
/**
* #ORM\ManyToOne(targetEntity="Lan\SecurityBundle\Entity\User", inversedBy="links")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\ManyToMany(targetEntity="Lan\CrmBundle\Entity\LinkTag", inversedBy="links")
* #ORM\JoinTable(
* name="LinkTagToLink",
* joinColumns={#ORM\JoinColumn(name="link_id", referencedColumnName="id", nullable=false)},
* inverseJoinColumns={#ORM\JoinColumn(name="link_tag_id", referencedColumnName="id", nullable=false)}
* )
*/
private $tags;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set url
*
* #param string $url
* #return Link
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set description
*
* #param string $description
* #return Link
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set created_at
*
* #param \DateTime $createdAt
* #return Link
*/
public function setCreatedAt($createdAt)
{
$this->created_at = $createdAt;
return $this;
}
/**
* Get created_at
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->created_at;
}
/**
* Set user
*
* #param \Lan\SecurityBundle\Entity\User $user
* #return Link
*/
public function setUser(\Lan\SecurityBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \Lan\SecurityBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Constructor
*/
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add tags
*
* #param \Lan\CrmBundle\Entity\LinkTag $tags
* #return Link
*/
public function addTag(\Lan\CrmBundle\Entity\LinkTag $tags)
{
$this->tags[] = $tags;
return $this;
}
/**
* Remove tags
*
* #param \Lan\CrmBundle\Entity\LinkTag $tags
*/
public function removeTag(\Lan\CrmBundle\Entity\LinkTag $tags)
{
$this->tags->removeElement($tags);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
My entire controller:
namespace Lan\CrmBundle\Controller;
use Lan\CrmBundle\Entity\Link;
use Lan\CrmBundle\Form\LinkType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class LinkController extends Controller {
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$tags = $em->getRepository('LanCrmBundle:LinkTag')->findAll();
if($request->query->has('tags')) {
$tags = $request->query->get('tags');
//var_dump($tags); die();
$links = $em->getRepository('LanCrmBundle:Link')->findByTags($tags);
var_dump($links); die();
} else {
$links = $em->getRepository('LanCrmBundle:Link')->findBy(
array(),
array('id' => 'desc')
);
}
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$links,
$this->get('request')->query->get('page', 1),
4
);
return $this->render('LanCrmBundle:Link:index.html.twig', compact('pagination', 'tags'));
}
}
But I got this error:
ContextErrorException: Notice: Undefined index: joinColumns in ...
I think I can't use ->findByTags() like I want...
Thanks in advance!
I don't think you can findByTags because SF2 won't know if you want AND or OR for tags.
The only solution I can suggest you is that you need to query from link_tag table (use repository LanCrmBundle:LinkTag) manually to get all link.id then continue to retrieve link data using LanCrmBundle:Link.
I found a way but not really what I want :
public function findByUsersAndTags($users, $tags)
{
$queryBuilder = $this->createQueryBuilder('l')
->leftJoin('l.tags', 't')->addSelect('t')
->leftJoin('l.user', 'u')->addSelect('u')
->orderBy('l.id', 'desc');
if($users) {
foreach($users as $user_id) {
$queryBuilder->orHaving('u.id = :id')->setParameter('id', $user_id);
}
}
if($tags) {
foreach($tags as $tag_id) {
$queryBuilder->orHaving('t.id = :id')->setParameter('id', $tag_id);
}
}
return $queryBuilder->getQuery()->getResult();
}

How to access form elements in controller symfony2

I've generated the form through entity generator,
I've extended the Cobrand Controller
class CobrandController extends Controller
{
public function createAction()
{
// the key used to lookup the template
$templateKey = 'edit';
if (false === $this->admin->isGranted('CREATE')) {
throw new AccessDeniedException();
}
$object = $this->admin->getNewInstance();
$this->admin->setSubject($object);
/** #var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
$form->setData($object);
if ($this->getRestMethod()== 'POST') {
// $cobrand = new Cobrand();
$em=$this->getDoctrine()->getManager();
$cobrand=new Cobrand($em);
$form->bind($this->get('request'));
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
$this->admin->create($object);
if ($this->isXmlHttpRequest()) {
return $this->renderJson(array(
'result' => 'ok',
'objectId' => $this->admin->getNormalizedIdentifier($object)
));
}
$this->addFlash('sonata_flash_success','flash_create_success');
// redirect to edit mode
return $this->redirectTo($object);
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash('sonata_flash_error', 'flash_create_error');
}
} elseif ($this->isPreviewRequested()) {
// pick the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$view = $form->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($view, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), array(
'action' => 'create',
'form' => $view,
'object' => $object,
));
}
}
Here's my entity code,
class Cobrand
{
/**
* #var \DateTime
*/
private $created;
/**
* #var \DateTime
*/
private $updated;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $code;
/**
* #var string
*/
private $logo;
/**
* #var boolean
*/
private $cobrandedProductsOnly;
/**
* #var integer
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $productPrices;
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $promocodes;
/**
* Constructor
*/
public function __construct()
{
$this->productPrices = new \Doctrine\Common\Collections\ArrayCollection();
$this->promocodes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Set created
*
* #param \DateTime $created
* #return Cobrand
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param \DateTime $updated
* #return Cobrand
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set name
*
* #param string $name
* #return Cobrand
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set code
*
* #param string $code
* #return Cobrand
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* #return string
*/
public function getCode()
{
return $this->code;
}
}
User will enter the values on the form and I wan't to access those values in the createAction method of controller.
I want to change the value of $name in the controller.
How to access this ?
Thanks,
Faisal Nasir
Try this after binding request
$form->get('name')->setData('new value');

Resources