Symfony2 __toString() generation - php

Does Symfony2 have an automatic __toString() generation based on the entity fields, or an annotation to say that the __toString() should be generated, similar to Java Roo?

I cannot find such a feature under the annotations reference, and the consensus among the Google Group seems to side with defining __toString() on the object.
If you use an IDE such as Net Beans, a simple CTRL+SPACE hotkey and click will automatically generate the __toString() for you, you'd simply need to fill out the refence to whichever attribute you want to use to represent the object.
Furthermore, you could take that one step further and define an Entity template (which is what I do in Net Beans). Something like this could save you some time, keeping in mind Doctrine2 is my ORM in this example, and I use the annotations method of defining my entities:
<?php
namespace Foo\BarBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
//use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class ${name}
{
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
public function __toString()
{
//return $this->get();
}
}
This automatically fills out the class name and has ArrayCollection commented out (so I can easily add that in if the entity requires it). This would leave you with simply needing to fill in the rest of whatever method you'd like to use for __toString();
${name} is a template variable in NetBeans.

Related

Symfony doctrine lazy load properties

I have an entity that stores large files as blobs to the DB.
I would now like to get Symfony to never ever load these blobs unless I specifically request them via the appropriate getter.
In essence I want the same idea as lazy-loading relationships but for a string property.
What I have tried so far is to put all my other properties that hold the file meta data into a trait and then apply that trait to two entities.
namespace App\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
trait DocumentMetaData
{
/**
* #var int|null
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime|null
*
* #ORM\Column(type="datetime")
*/
private $date_uploaded;
}
One entity has nothing to it but the trait...
namespace App\Entity;
use App\Entity\Traits\DocumentMetaData;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="documents")
* #ORM\Entity()
*/
class Document
{
use DocumentMetaData;
}
...the other has the added blob property:
namespace App\Entity;
use App\Entity\Traits\DocumentMetaData;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="documents")
* #ORM\Entity()
*/
class DocumentFile
{
use DocumentMetaData;
/**
* #var string|null
*
* #ORM\Column(type="blob")
*/
private $blob;
}
Now, if I don't want the blob to be loaded, for example for a listing of files, I simply use the entity that doesn't have the blob.
This approach sort of works but causes issues as I need to point both entities at the same table (see the class level ORM annotations).
Specifically, it makes doctrine freak out when running migrations:
The table with name 'myapp.documents' already exists.
That makes perfect sense and really I'm hoping that someone can point me to a nicer solution.
How can I tell doctrine not to load the blob unless its explicitly asked for?
So as per the comments on my question - the way to do this so that migrations do not break is to leverage doctrine's ability to lazy load relationships between tables.
Basically I had to go and create a new entity that only holds my giant blobs and then establish a one to one relationship between the original entity and the blob entity.
I then set that relationship to load EXTRA_LAZY and as a result I can now control when precisely the blobs of giant data should be loaded.
I don't think this is ideal in terms of normalising the DB design but it works a lot better than anything else so happy with that.

Doctrine2: persisting parent entity given child class

I've got a User Entity defined (mapping in yml)
namespace My\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class User
{
...
And I created a child class that inherits from that entity, so that I can add some custom validation methods and a few fields that I need but do not need to be persisted (e.g. ConfirmPassword, ConfirmEmail fields)
namespace My\SecondBundle\EditModels;
use My\CoreBundle\Entity\User;
class UserModel extends User
{
When the user submit a registration form, I map the request to a UserModel entity, and if it is valid I try to persist the user.
The following code throws an exception
$entityManager->persist($userModel);
//=>The class 'My\SecondBundle\EditModels\UserModel' was not found in the chain configured namespaces My\CoreBundle\Entity
Question: How can I persist $userModel (instance of UserModel) as a User entity class? Possible options:
Do not use an inherited class and add custom fields and validation method to the User entity itself
Copy the fields from the UserModel to the User entity and persist the user entity
I don't think I should use Doctrine inheritance mechanism as I do not want to save the extra fields.
Thank you
I think your problem here, is that you've just configured My\CoreBundle\Entity namespace in Doctrine2, but the entity you actually want to persist is located in My\SecondBundle\EditModels.
Usually when inheriting classes marked as #ORM\Entity() the class you are extending from must have the class annotation #ORM\MappedSuperclass(). But normally you use this for single table inhertiance e.g., not for your usecase.
In my opinion the approach to split database related attributes from the others, is not affordable. I would keep validation related stuff in the model itself - you need it in your create/update action.
I'm not familiar with XML configuration, but when using annotations you need to mark each property to be mapped with database (using #ORM\Column()). So Doctrine will ignore all the other attributes and methods entirely.
So here I share my recently developed AbstractModel for you, to see how I've implemented validation (with respect/validation):
<?php
namespace Vendor\Package\Model;
use Doctrine\ORM\Mapping as ORM;
/**
* Abstract Model
*
* #ORM\MappedSuperclass()
*/
abstract class AbstractModel
{
/**
* #var \Respect\Validation\Validator
*/
protected $validator;
/**
* AbstractModel constructor
*/
public function __construct()
{
$this->validator = static::validation();
}
/**
* Defines validation for this model
*
* #return \Respect\Validation\Validator
*/
public static function validation() : \Respect\Validation\Validator
{
return \Respect\Validation\Validator::create();
}
/**
* Executes validations, defined in validation method.
*
* #return bool
*/
public function isValid() : bool
{
if (is_null($this->validator)) {
$this->validator = new \Respect\Validation\Validator();
$this->validation();
}
return $this->validator->validate($this);
}
}
A model which extends from the AbstractModel needs to implement a static validate method, to define class validation:
<?php
namespace Vendor\Package\Model;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
* #ORM\Table(name="my_model")
*/
class MyModel extends AbstractModel
{
/**
* #var string
* #ORM\Column(type="string")
*/
private $name;
/**
* Defines validation for this model
*
* #return \Respect\Validation\Validator
*/
public static function validation() : \Respect\Validation\Validator
{
return \Respect\Validation\Validator::create()
->attribute('name', \Respect\Validation\Validator::notEmpty()->stringType()->length(null, 32))
;
}
// getter, setter, ...
}
Each entity, persisted to database, will have the $validator property and all these methods, but because I left annotations here (and pretty sure this also works with xml/yaml) Doctrine ignores it.
And this way you also keep validation related stuff out of the model class itself, which is good for readability. The validation itself should be defined in the model itself, imho. But this respect/validation framework is neat way to achive this. Hope this helps :)

Symfony2: Custom identifier in Sonata entities

I have an entity with a custom id (i.e. UUID) generated on __construct function.
namespace AppBundle\Entity;
use Rhumsaa\Uuid\Uuid;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Person
{
/**
* #ORM\Id
* #ORM\Column(type="string")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
}
This entity is used in sonata and also in other part of the project. I need this entity to have id before persisting and flushing it, so I can not use a an auto-increment.
So, the problem is sonata don't let me create entities because it takes the create option as and edit on executing because that entity already has an id, but this entity does not exists at this moment, so it fails.
The problem isn't the library for generating UUID, any value for 'id' fails.
Anyone know how to solve it? Another similar approach to solve the problem?
You shouldn't set your id in the constructor, but rather use the prePersist Doctrine event to alter your entity before persisting it for the first time.
You may use annotations to do so, see the Doctrine Documentation on prePersist.
The issue with setting the id in the constructor is that you may override it when you're retrieving it from the database, in which case it will be incorrect.

Doctrine classes, can I add custom functions?

I'm having a difficult time finding if I can add custom functions to doctrine classes.
Lets say I have
use Doctrine\ORM\Mapping as ORM;
/**
* Map
*/
class Map
{
/**
* #var integer
*/
private $id;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
In my classes I would like some custom functions that return values that do not per se need to be stored in databse but merely provide a checking of certain values functionality.
For examply I would like to add a function isAboveTen();
function isAboveTen()
{
return this->id > 10;
}
Can I just go ahead and do this or do I need to define them as a special field in the xml file or annotations?
You can safely add functions working on simple member types, Doctrine will ignore them if you do not add any annotations.
The question whether you should avoid doing this depends on your overall architecture and coding guidelines. As mentioned in the comments both flavors with logic possibly inside vs outside the entities exist.
However, you should keep in mind that:
All persistent properties/field of any entity class should always be private or protected, otherwise lazy-loading might not work as expected. In case you serialize entities (for example Session) properties should be protected (See Serialize section below).
Which is described in the documentation. Since you are accessing these members inside your class, magic methods like __get() will not be called.

Symfony 2 unique validator

I'm trying to validate a form with some fields that need to be unique - username and email address. If I submit the form I get a database error. I want to use a validator like I did for everything else - right now I'm trying to use custom getters and isvalidUsername functions in the object and I'm not sure if using the entity manager in the object is the best way to do this. Heres what I'm working with so far...
Frontend\UserBundle\Entity\User:
properties:
email:
- NotBlank: ~
- Email: ~
username:
- NotBlank: ~
getters:
validUsername:
- "True": { message: "Duplicate User detected. Please use a different username." }
validEmail:
- "True": { message: "Duplicate email detected. Please use a different email." }
There are built in unique validators in the fosuserbundle but I haven't been able to figure out how to use them.
I know that this is an old question but I've just had to work this out so I thought I would share my solution.
I prefer not to use any bundles to handle my users so this is my manual approach:
<?php
namespace MyCorp\CorpBundle\Entity;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/** User - Represent a User object */
class User implements AdvancedUserInterface {
/** #var integer $id */
private $id;
/** #var string $email */
private $email;
/** Constructor, Getters, Setters etc... */
/** Set a list of rules to use when validating an instance of this Class
#param Symfony\Component\Validator\Mapping\ClassMetadata $metadata */
public static function loadValidatorMetadata(ClassMetadata $metadata) {
$metadata->addPropertyConstraint('email', new MaxLength(255));
$metadata->addPropertyConstraint('email', new NotBlank());
$metadata->addPropertyConstraint('email', new Email());
$metadata->addConstraint(new UniqueEntity(array(
"fields" => "email",
"message" => "This email address is already in use")
));
}
}
As you can see I define my validation in the model itself. Symfony will call loadValidatorMetadata to let you load validators.
First of all, I'd recommend you using FOSUserBundle. It's quite flexible and you can save yourself some time you'd spend by fixing subtle bugs and testing if everything really works as intended.
Anyway, if you really want to build it yourself, you can at least inspire by bundle I mentioned above. They define custom validator and check for uniqueness in UserManager (validateUnique). Additionally, you have to register it as a service to provide UserManager via constructor injection. Then you just use it as a normal class validator.
There's the UniqueEntity validation constraint for ensuring that the user provides a unique value for a particular property.
Please refer to the documentation for examples using the various formats that Symfony supports. Here's an example using annotations:
// Acme/UserBundle/Entity/Author.php
namespace Acme\UserBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
// DON'T forget this use statement!!!
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity("email")
*/
class Author
{
/**
* #var string $email
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\Email()
*/
protected $email;
// ...
}

Categories