I have implemented entities within the Symfony2 framework that are annotated to be used by Doctrine. For example:
/*
* class description
*
* #ORM\Entity(repositoryClass="Website\ContentBundle\Repository\ContentRepository")
* #ORM\HasLifecycleCallbacks()
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"article" = "Article"})
* #ORM\Table(name="content",indexes={#ORM\index(name="id_UNIQUE",columns={"id"})})
*/
class Content {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
}
When I run Doxygen on my source code, the documentation is not very readable. I have tried to define aliases for each #ORM* symbol; for example "ORM=ORM", "Entity=Entity", and so on. But this does not work. For the above mentioned class Doxygen returns
...
ORMEntity(repositoryClass="Website\ContentBundle\Repository\ContentRepository") ORM() ORM("SINGLE_TABLE") ORM(name="type", type="string") ORM({"article" = "Article", "picture_series" = "PictureSeries", "event" = "Event", "invitation" = "Invitation"}) ORMTable(name="content",indexes={ORM(name="id_UNIQUE",columns={"id"})})
With respect to method
/**
* sets the given id
*
* #param number $id
* #return \Website\ContentBundle\Entity\Content
*/
public function setId($id) {
$this->id = $id;
return $this; // fluent interface
}
Doxygen creates
setId ($ id)
sets the given id
Parameters:
number $id
Returns:
Why does it not show the \Website\ContentBundle\Entity\Content after "Returns:"?
Maybe someone can give me a hint or a link on how to configure Doxygen such that it can handle the #ORM annotations appropriately.
THX in advance!
With respect to the question
Why does it not show the \Website\ContentBundle\Entity\Content after Returns:?
This is probably because doxygen commands start with a \, do doxygen thinks you are calling some commands which it does not recognise, and so presumably strips from the documentation and does nothing with.
You were on the right line with attempting to use the ALIASES configuration file option. However, instead of defining ORM=ORM try using ORM=\#ORM. I got your example source code to be documented by doxygen without any warnings by defining the following aliases:
ALIASES = "ORM=\#ORM"
ALIASES += "Entity=\\Entity"
ALIASES += "InheritanceType=\\InheritanceType"
ALIASES += "DiscriminatorColumn=\\DiscriminatorColumn"
ALIASES += "DiscriminatorMap=\\DiscriminatorMap"
ALIASES += "Table=\\Table"
ALIASES += "Id=\\Id"
ALIASES += "Column=\\Column"
ALIASES += "GeneratedValue=\\GeneratedValue"
ALIASES += "index=\\index"
ALIASES += "HasLifecycleCallbacks=\\HasLifecycleCallbacks"
ALIASES += "Repository=\\Repository"
ALIASES += "ContentRepository=\\ContentRepository"
ALIASES += "ContentBundle=\\ContentBundle"
ALIASES += "Website=\\Website"
ALIASES += "Content=\\Content"
Here \\, \# are actually doxygen commands for printing the backslash \ and # characters respectively.
Note, however, that the #ORM... directives will all appear on the same line. I'm not sure how to avoid this (update: see my edit below). Does anyone else have any ideas?
Finally, as a side note, your documentation for $id should be
* #param $id number
note the order of $id and number. See the documentation for \param.
Edit: An alternative way of doing this would be to wrap the Doctrine relavent parts in \verbatim and \endverbatim tags. This will preserve the line breaks in your class description which will be more readable.
Related
I'm using the Form Builder from Symfony and the class I created for the Form is like the next:
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
class Upload
{
/**
* #Assert\File(
* maxSize = "4000k",
* mimeTypes = {"text/plain", "text/xml", "application/xml", "text/csv",
* "application/EDI-consent", "application/EDIFACT", "application/EDI-X12"
* "application/vnd.mseq", "application/json-seq",
* "application/vnd.sealed-xls",
* "application/pdf", "application/x-pdf",
* "application/zip"},
* mimeTypesMessage = "Please upload a valid format file"
* )
* #var file
*/
protected $file;
...
I need to accept these next extensions:
'txt,dat,xml,csv,edi,seq,xls,pdf,zip'
Is enough like this? Is anything left to add? I'm wrong with something?
Symfony Docs recommend to get the results from here:
https://www.iana.org/assignments/media-types/media-types.xhtml
In my opinion it should work properly. Maybe you should add application/octet-stream and zz-application/zz-winassoc-dat to match also dat file extension. And... test your code with different files in order to see if it works.
I have entity with uniq field, inviteCode. And when I create new entity I want set automatic some random code, but this code must be different from exist in db, what do you thing, what practices about that you know ?
/**
* #ORM\Table(name="users")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #AssertBridge\UniqueEntity(
* groups={"registration"},
* fields="inviteCode",
* errorPath="not valid",
* message="This inviteCode is already in use."
* )
*/
class User extends AbstractUser implements UserInterface
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=500, unique=true)
* #Annotation\SerializedName("_invite_code")
*/
private $inviteCode;
I found something like that
private function calculateReference($number)
{
$multipliers = array(7,3,1);
$length = strlen($number);
$numberArr = str_split($number);
$sum = 0;
for ($i = $length - 1; $i >= 0; --$i)
{
$sum += $numberArr[$i] * $multipliers[($length - 1 - $i) % 3];
}
return $number.(10 - $sum % 10) % 10;
}
first get max id from table then call function calculateReference with id and then setInviteCode.
But I believe doctrine have something exist for this issue or maybe somebody have good example for this
Someone provided a great answer here https://stackoverflow.com/a/13917309/4173130.
But like he said at the end, you don't need doctrine for such a simple feature. Generating the code in the constructor is an efficient, simple and clear solution.
You can use a UUID library like ramsey/uuid. Then you would be able to generate any random code with Uuid::uuid4();.
Another solution is to use random_bytes() with base64_encode : base64_encode(random_bytes(32)).
Please don't try to create a new function to generate random values. Most of time it is not secure, see https://www.owasp.org/index.php/Insecure_Randomness.
Why not using a uuid? It is included in php as a core function and i believe it suits your needs.
Check in the official documentation here
I'm quite new with Doctrine, so I hope someone can help me or redirect me to the good documentation page.
I'm building an app with two entity (I reduce for explanations) :
- Tender
- File
For each tender, we can have one or more files. So I made the following objects.
Tender:
<?php
namespace TenderBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="tender")
*/
class Tender
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $tender_id;
/**
* #ORM\Column(type="array")
* #ORM\ManyToOne(targetEntity="File", inversedBy="tenders")
* #ORM\JoinColumn(name="tender_files", referencedColumnName="file_id")
*/
private $tender_files;
}
File:
<?php
namespace TenderBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #ORM\Table(name="file")
*/
class File
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $file_id;
/**
* #ORM\OneToMany(targetEntity="Tender", mappedBy="tender_files", cascade={"persist", "remove"})
*/
private $file_tender;
}
First question: is it the right way to do this?
(of course, i've created the methods to get and set attributes, but they're basic).
When I persist each of my File object i'm trying to add then to my Tender instance. But to do this, I need to make $tender_files public and do this:
$tender->tender_files[]
This is not a viable solution for me because I need all my fields are private and I want to recover my object when I try to call this:
$this->getDoctrine()->getManager()->getRepository('TenderBundle:Tender')->find($id)->getTenderFiles()->getFileName();
So, I'm explaining and asking to find the right way to do what I want. I hope what i need is clear and i'm here to answers questions or show more code if needed.
Thanks!
Like Richard has mentioned, you're missing getters and setters which are declared to be public. They'll have access to your private variables. The quick way to do this with symfony:
php app/console doctrine:generate:entities
It'll generate something like this:
public function addTenderFile(\TenderBundle\Entity\File $file)
{
$this->tender_files[] = $file;
return $this;
}
/**
* Remove
*/
public function removeTenderFile(\TenderBundle\Entity\File $file)
{
$this->tender_files->removeElement($file);
}
/**
* Get
*/
public function getTenderFiles()
{
return $this->tender_files;
}
It's good practice if you're a beginner to see how your code lines up with the auto generator. Once you understand what's going on, just let the generator do the grunt work.
You should have a setter and getter in your File entity similar to this:
public function setTender(\Your\Namespace\Tender $tender)
{
$this->tender = $tender;
return $this;
}
public function setTender()
{
return $this->tender;
}
So when you instance (or create) File, you can go like so:
$file = new File(); // or file fetched from DB, etc.
// here set $file properties or whatever
$tender->setFile($file);
$entityManager->persist($tender);
$entityManager->flush();
Then your tender will be properly associated with your file.
Similarly from the File end, you should be able to do:
$file->addTender($tender);
$entityManager->persist($file);
$entityManager->flush();
And your tender will be added to your File->tenders collection.
For more information the documentation is very useful and has more or less everything you need to get started.
Also, save yourself manually creating getters and setters by using generate:doctrine:entity
This is incorrect:
/**
* #ORM\Column(type="array")
* #ORM\ManyToOne(targetEntity="File", inversedBy="tenders")
* #ORM\JoinColumn(name="tender_files", referencedColumnName="file_id")
*/
private $tender_files;
You can't persist an array to your database. A database row is one entity and it's corresponding attributes. If a tender can have many files, then this relationship should be:
* #ORM\OneToMany
Likewise for the File entity. If many files can have one Tender then it's relationship should be:
* #ORM\ManyToOne
For relationship mapping using Doctrine, it's helpful to read left-to-right with the entity YOU'RE CURRENTLY IN being on the left, and the entity you're setting as a variable being on the right.
If you're in Tender reading left-to-right Tender may have "OneToMany" files. And File(s) may have ManyToOne Tender. Doctrine Association Mapping
I have just installed the doctrine extensions to use Sluggable.
I make this :
composer.json
"stof/doctrine-extensions-bundle": "1.2.*#dev"
AppKernel.php
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
app/config/config.yml
stof_doctrine_extensions:
orm:
default:
sluggable: true
Djoo\AppliBundle\Entity\Nomenclature.php
namespace Djoo\AppliBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\SmallIntType;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Nomenclature
*
*
* #ORM\Table(name="app_nomenclature")
* #ORM\Entity
*/
class Nomenclature
{
.....
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=200, nullable=false)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="finess", type="string", length=10, nullable=true)
*/
private $finess;
/**
* #Gedmo\Slug(fields={"titre","finess"},suffix=".html")
* #ORM\Column(length=128, unique=true,nullable=true)
*/
private $slug;
public function getSlug() {
return $this->slug;
}
public function setSlug($slug){
$this->slug = $slug;
return $this;
}
}
In my controller i make this to generate slug for old values in my datatable :
$filterBuilder = $this->get('doctrine.orm.entity_manager')>getRepository('DjooAppliBundle:Nomenclature')->createQueryBuilder('e')->orderBy('e.titre', 'asc');
$query = $filterBuilder->getQuery();
$nomenclatures = $query->getResult();
foreach($nomenclatures as $nomenclaturee){
$nomenclature->setSlug(null);
$this->get('doctrine.orm.entity_manager')->persist($nomenclature);
$this->get('doctrine.orm.entity_manager')->flush();
}
I have no error, but my old values are a null slug. I try to create a new element and i have a good slug. Have you and idea ?
Thanks
To change the slug you must change the related property. You can add a space at the end of $titre, save it, change it back and save it again. That will flush the slugs.
$uow = $em->getUnitOfWork();
$uow->propertyChanged($entity, 'slug', NULL, NULL);
$uow->scheduleForUpdate($entity);
$em->flush();
Why it didn't work for OP, but worked for others (eg. #gregor):
When creating slug, your first instinct is to create slug property with this column configuration:
..
#ORM\Column(unique=true, nullable=false)
private $slug;
..
When running app/console doctrine:schema:update and this will result in 2 sql statements:
ALTER TABLE ... ADD slug ... NOT NULL
CREATE UNIQUE INDEX...`.
By default column slug will be filled with value '' (empty string) which would make 2nd statement to fail with (Duplicate entry '') error. Now you have two choices:
Choice A: Ignore failure of the 2nd statement
If you ignore the error, and later try generating slugs manually using the documented method $entity->setSlug(null) everything would work. It would work because by using $entity->setSlug(null) you would let Doctrine know that propertyslug was changed (from '' to null) and this in turn would trigger internally $uow->propertyChanged() and $uow->scheduleForUpdate() (Thanks to #Sebastian Radu for his example). The Sluggable extension will notice this change as well and will regenerate the slug. Now as all the slugs are unique, next time you run app/console doc:schema:update it will succeed in creating index on slug and your schema will be fully in sync.
Choice B: Modify slug field to be nullable
After noticing error your instinct would be to mark slug field as nullable, so that index creation succeeds:
..
#ORM\Column(unique=true, nullable=true)
private $slug;
..
This would result in slug column having NULL as it's default value. Now as you try using documented $entity->setSlug(null) approach it won't work (just as OP has posted). This happens because when $entity->slug property is already NULL. Thus when you use $entity->setSlug(null) no changes are detected by Doctrine, and thus Sluggable regeneration behaviour is never triggered. In order to trigger the changes there were two answers:
hack with adding space to the slug source properties $entity -> setTitre($titre." "); (but this would result in extra space you would have to trim after)
approach by #Sebastian Radu, where he shows how to tell Doctrine directly that the field was changed (I personally prefer this one and wonder why it was unfairly downvoted)
Hope this helps you understand a bit better the inner workings of Doctrine and extensions.
The sluggable documentation states the following:
In case if you want the slug to regenerate itself based on sluggable
fields, set the slug to null.
<?php
$entity = $em->find('Entity\Something', $id);
$entity->setSlug(null);
$em->persist($entity);
$em->flush();
It does work for me.
Is it possible to create custom metadata information in the entities? Something that would use the already present functionality of using either Annotation, yml or xml to store metadata about the entity.
Example:
/**
* #var string
*
* #ORM\Column(name="text", type="text")
* #CUSTOM\Meta(key="value") // <-- Extra information
*/
protected $text;
For what I've been researching, it seems that I should use the functionality of ClassMetadataFactory. Is it possible, or would I have to make it from scratch?