i've extended FOSUserBundle with my custom User Entity in this way:
<?php
namespace Hu\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Entity\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="fname", type="string", length=255)
* #Assert\NotBlank()
*/
private $fname;
// other fields...
/**
* Set fname
*
* #param string $fname
* #return UserEntity
*/
public function setFname($fname)
{
$this->fname = $fname;
return $this;
}
/**
* Get fname
*
* #return string
*/
public function getFname()
{
return $this->fname;
}
public function __construct()
{
parent::__construct();
// ...
$this->fname = setFname($fname);
}
}
But when i try to load the /register route or to add a new user by:
php app/console fos:user:create
Symfony returns me:
FatalErrorException: Error: Call to undefined function Hu\UserBundle\Entity\setFname() in /Library/WebServer/Documents/sfprojects/quattro/src/Hu/UserBundle/Entity/User.php
What's wrong in the constructor? What i miss?
Thanks a lot,
setFname isn't function, but it is method in your class.
so to execute it you need use $this->setFname($fname).
Also in constructor you shouldn't run methods like setters or getters (if they don't do anything except set variable or get variable)
Your constructor should look like:
public function __construct()
{
parent::__construct();
$this->fname = $fname;
}
the commande " php app/console fos:user:create " uses the setters of the entity User to affect data inputs from the terminal ( in the same way that Forms uses them to affect the data to an object from the < input ... > tag ) .
You can add setters for all the fields you added to your User class to solve this problem
exemple :
/**
* #var string
*
* #ORM\Column(name="fname", type="string", length=255)
* #Assert\NotBlank()
*/
private $fname;
public function setFname($fname) {
$this->fname = $fname;
}
Related
I am using Knp/DoctrineBehaviors to translate my database content.
I followed the manual and created 2 entities, 1 for non-translatable content and the other for translatable fields.
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
/**
* Class Test
* #package App\Entity
* #ORM\Entity()
*/
class Test implements TranslatableInterface
{
use TranslatableTrait;
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Id()
*/
private $id;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
}
and the Translation entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslationTrait;
/**
* Class TestTranslation
* #package App\Entity
* #ORM\Entity()
*/
class TestTranslation implements TranslationInterface
{
use TranslationTrait;
/**
* #var
* #ORM\Column(type="string")
*/
private $name;
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
}
I also added the Bundle in my bundles.php file.
But when I run the command php bin/console doctrine:schema:update --force to create the table in give the error: No identifier/primary key specified for Entity "App\Entity\TestTranslation". Every Entity must have an identifier/p rimary key.
A primary key is missing in your entity "TestTranslation".
In doctrine every entity class must have an identifier/primary key. You can select the field that serves as the identifier with the #Id() annotation or in your case : #ORM\Id().
For example, add this to your entity :
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Id()
*/
private $id;
And then, you can play the command: php bin/console d:s:u --force
In my case, Symfony 3.4, I forgot to register the bundle and set the config.yml as:
knp_doctrine_behaviors:
translatable: true
I use FOSRestBundle with JMSSerializerBundle in my web service.
Here is a function in my action controller that returns a list of roles. I don't know how to return a "parent_id" field in this list.
<?php
use FOS\RestBundle\Controller\FOSRestController;
class RoleController extends FOSRestController
{
...
/**
* List all roles.
*
* #Annotations\View()
*
* #return array
*/
public function getRolesAction()
{
$roles = $this->repository->findRoles();
$view = $this->view($roles, 200);
return $this->handleView($view);
}
...
}
Here is my role entity. It has properties: id, name and parent. Parent is a role.
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\RoleRepository")
*/
class Role
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Role")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
// + getters and setters
...
}
Here is my serializer config file:
AppBundle\Entity\Role:
exclusion_policy: ALL
properties:
id:
expose: true
type: integer
xml_attribute: true
name:
expose: true
type: string
Now getRolesAction in action controller will output JSON with id and name properties. How to add parent_id property to the serializer output?
You could use something like:
/**
* #Serializer\VirtualProperty
* #Serializer\Type("integer")
* #Serializer\SerializedName("parent_id")
*
* #return integer
*/
public function getParentId()
{
return $this->parent->getId();
}
I can't tell you how to transfer the annotation in your config (probably YAML) format, but you'll be able to do that.
IMPORTANT
This only works for serialization - so don't expect this attribute to be deserialized later!!
Alternative:
If you only want to achieve the result you were asking for you could add this to your attribute:
/**
* #Accessor(getter="getParentId",setter="setParent") */
* #Serializer\Type("integer")
* #Serializer\SerializedName("parent_id")
*/
private $parent;
and additionally another getter:
/**
*
* #return integer
*/
public function getParentId()
{
return $this->parent->getId();
}
With the accessor you tell JMS from which getter to get the result to serialize while still being able to use the setter for deserialization.
I'm using the excellent doctrine extension uploadable. I can upload one file per entity just fine, but how can I upload two different files on the same entity?
* #Gedmo\Uploadable(path="uploads/articles", appendNumber=true, filenameGenerator="SHA1")
class Article
{
* #ORM\Column(name="photo", type="string", length=255)
* #Gedmo\UploadableFilePath
private $photo
* #ORM\Column(name="pdf", type="string", length=255)
* #Gedmo\UploadableFilePath
private $pdf
On my controller I have:
$uploadableManager->markEntityToUpload($article, $article->getPhoto());
$uploadableManager->markEntityToUpload($article, $article->getPdf());
Only the last file is uploaded and saved to the database. How can I do this?
You probably confused something.
You have Article entity with two fields: photo and pdf, but there is no $materia entity. You probably should change $materia to $article. But this won't work because #Uploadable cannot upload multiple files for the same entity.
Hint: use VichUploaderBundle for Doctrine file uploads handling
UPD: Here is example class.
<?php
namespace Acme\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #ORM\Table(name="article")
* #Vich\Uploadable
*/
class Article
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ..... other fields
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="article_photo", fileNameProperty="photoName")
*
* #var File
*/
private $photoFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $photoName;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="article_pdf", fileNameProperty="pdfName")
*
* #var File
*/
private $pdfFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $pdfName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* #param \DateTime $updatedAt
* #return Article
*/
public function setUpdatedAt(\DateTime $updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* 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 $photo
*
* #return Article
*/
public function setPhotoFile(File $photo = null)
{
$this->photoFile = $photo;
if ($photo) {
// 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 \DateTime('now');
}
return $this;
}
/**
* #return File
*/
public function getPhotoFile()
{
return $this->photoFile;
}
/**
* #param string $photoName
*
* #return Article
*/
public function setPhotoName($photoName)
{
$this->photoName = $photoName;
return $this;
}
/**
* #return string
*/
public function getPhotoName()
{
return $this->photoName;
}
/**
* 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 $pdf
*
* #return Article
*/
public function setPdfFile(File $pdf = null)
{
$this->pdfFile = $pdf;
if ($pdf) {
// 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 \DateTime('now');
}
return $this;
}
/**
* #return File
*/
public function getPdfFile()
{
return $this->pdfFile;
}
/**
* #param string $pdfName
*
* #return Article
*/
public function setPdfName($pdfName)
{
$this->pdfName = $pdfName;
return $this;
}
/**
* #return string
*/
public function getPdfName()
{
return $this->pdfName;
}
}
And you need to configure VichUploader this way:
# app/config/config.yml
vich_uploader:
db_driver: orm
mappings:
article_photo:
uri_prefix: /images/articles/photos
upload_destination: %kernel.root_dir%/../web/images/articles/photos
article_pdf:
uri_prefix: /images/articles/pdfs
upload_destination: %kernel.root_dir%/../web/images/articles/pdfs
Be attentive. You can get confused with configuration, mappings, methods... just read manual carefully and thoughtly. https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md
Whats the best practice to check if entity fields exist before persisting it.
Here's the example
Entity
class Pile{
/**
* #var \ABC\CoreBundle\Entity\Record
*
* #ORM\OneToMany(targetEntity="Record")
*
*/
private $records;
/**
* #var \CSC\CoreBundle\Entity\Project
*
* #ORM\ManyToOne(targetEntity="Project")
*
*/
private $project;
/**
* #var string
*
* #ORM\Column(name="Block", type="string", length=255)
*/
private $block;
/**
* #var string
*
* #ORM\Column(name="Type", type="string", length=255)
*/
private $type;
}
class Record{
/**
* #var \CSC\CoreBundle\Entity\Pile
*
* #ORM\ManyToOne(targetEntity="Pile")
*
*/
private $records;
}
There are two controllers that handle the CRUD of Pile and Records.
To create Pile there must not be any duplicate fields [project, block, type]
In Record Controllers I could create Pile together with Record.
Here's the problem where and when do I check the db if a similar Pile entity is created?
Whats the Best Practice?
Copy and paste the query checker in both controller?
Can I use $form->valid() to perform any check in PileType class?
Must I use a service and have both controller to call the service?
In entity life-cycle use pre-insert?
Thanks
Therefore, the fields must be unique?
If so, then it is very simple: UniqueEntity
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
...
/**
* #ORM\Entity
* #UniqueEntity(
* fields={"project", "block", "type"}
* )
*/
class Pile{
/**
* #var \ABC\CoreBundle\Entity\Record
*
* #ORM\OneToMany(targetEntity="Record")
*
*/
private $records;
/**
* #var \CSC\CoreBundle\Entity\Project
*
* #ORM\ManyToOne(targetEntity="Project")
*
*/
private $project;
/**
* #var string
*
* #ORM\Column(name="Block", type="string", length=255, unique=true)
*/
private $block;
/**
* #var string
*
* #ORM\Column(name="Type", type="string", length=255, unique=true)
*/
private $type;
}
You can use a custom validation constraint in your form, so that $form->isValid() will do the check.
Follow this documentation entry on How to create a Custom Validation Constraint to create the custom validator and then inject doctrine into it to do the check.
UPDATE: Well, I didn't know there was an UniqueEntity Constraint already included in Symfony.
To inject doctrine do the following:
services:
validator.unique.unique_pile:
class: ABC\CoreBundle\Validator\Constraints\UniquePileValidator
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: validator.constraint_validator, alias: unique_pile }
The validator class might then look like this:
// src/ABC/CoreBundle/Validator/Constraints/UniquePileValidator.php
namespace ABC\CoreBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class UniquePileValidator extends ConstraintValidator
{
protected $em;
function __construct($em) {
$this->em = $em;
}
public function validate($value, Constraint $constraint)
{
$repo = $this->em->getRepository('ABC\CoreBundle\Entity\Record');
$duplicate_project = $repo->findByProject($value);
$duplicate_block = $repo->findByBlock($value);
$duplicate_type = $repo->findByType($value);
if ($duplicate_project || $duplicate_block || $duplicate_type) {
$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
}
}
}
And to be complete, the constraint class:
// src/ABC/CoreBundle/Validator/Constraints/ContainsAlphanumeric.php
namespace ABC\CoreBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class ContainsAlphanumeric extends Constraint
{
public $message = 'This Pile already exists!';
public function validatedBy()
{
return 'unique_pile';
}
}
Should be nearly copy/pasteable...
I've looked at literally tons of questions/answers on Stack Overflow and other places on the web, but cannot find a resolution to this problem.
Before I explain the problem, I have:
stripped back my entities so that they only have the minimum attributes and methods
have cleared the doctrine query cache / metadata cache
dropped and recreated the schema
checked my spelling
I have the following two entities:
<?php
namespace Docker\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Source
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Project",inversedBy="sources")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $project;
}
<?php
namespace Docker\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Project
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Source", mappedBy="project")
*/
private $sources;
public function __construct() {
$this->sources = new ArrayCollection();
}
public function getSources() {
return $this->sources;
}
}
So a many 'sources' can belong to one 'project'.
In my controller I have:
$em = $this->getDoctrine()->getManager();
$project = $em->find('Docker\ApiBundle\Entity\Project', 1);
$sources = $project->getSources()->toArray();
I have tried lots of things but I always get:
Notice: Undefined index: project in /.../www/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1577
Like I say, I know there are a lot of questions going around about this, but none of the accepted answers fix my problem.
This all looks pretty fundamental to using Doctrine2 so not sure what I am doing wrong - it could be something really obvious.
Any help would be appreciated.
You have:
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Project",inversedBy="sources")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $project;
Remove:
#ORM\Column(type="integer")
from annotation.
If this is exactly your code, i dont see a namespace in your Project class. Try adding the namespace line "namespace Docker\ApiBundle\Entity;".
If this is the same folder no need for "use" but if it s part of other bundle or folder try putting a line like "use Docker\ApiBundle\Entity\Project;"
in your Source class. I Hope it helps..
Otherwise :
<?php
namespace Azimut\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Source
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private $name;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Azimut\ApiBundle\Entity\Project", inversedBy="sources")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $project;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set project
*
* #param integer $project
* #return Source
*/
public function setProject($project)
{
$this->project = $project;
return $this;
}
/**
* Get project
*
* #return integer
*/
public function getProject()
{
return $this->project;
}
}
<?php
namespace Azimut\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class Project
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Azimut\ApiBundle\Entity\Source", mappedBy="object", cascade={"all"})
*/
private $sources;
public function __construct() {
$this->sources = new ArrayCollection();
}
public function getSources() {
return $this->sources;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add sources
*
* #param \Azimut\ApiBundle\Entity\Source $sources
* #return Project
*/
public function addSource(\Azimut\ApiBundle\Entity\Source $sources)
{
$this->sources[] = $sources;
return $this;
}
/**
* Remove sources
*
* #param \Azimut\ApiBundle\Entity\Source $sources
*/
public function removeSource(\Azimut\ApiBundle\Entity\Source $sources)
{
$this->sources->removeElement($sources);
}
}
and little part of the controller:
public function helloAction()
{
$id = 1;
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('Azimut\ApiBundle\Entity\Project')->find($id);
$source1 = $em->getRepository('Azimut\ApiBundle\Entity\Source')->find(3);
$source2 = $em->getRepository('Azimut\ApiBundle\Entity\Source')->find(5);
$project->addSource($source1);
$sources = array();
$sources = $project->getSources();
var_dump($sources);
return ....
}
and this works just fine for me.