I noticed some weird things using Doctrine ORM's ArrayCollection in a PostgreSQL database (using this in a Symfony 3 project).
Take my User class with roles, It's initiated with a default role ROLE_USER and must be of type array.
class User implements UserInterface, Serializable
{
/**
* #var ArrayCollection
*
* #ORM\Column(name="roles", type="array")
*/
private $roles;
public function __construct()
{
$this->roles = new ArrayCollection();
$this->roles->add('ROLE_USER');
}
}
This is stored in the database as follows.
O:43:"Doctrine\Common\Collections\ArrayCollection":1:{s:53:"
Which will give a Serialization error when trying to logon as a User because clearly part of the ArrayCollection is missing in the database.
After googling for a solution I came across this Github issue. As I understand it, it's a bug and you can't use the ArrayCollection in a PostgreSQL environment.
Not defining the type in the #ORM\Column tag doesn't help either, it then stores the following in the database and Symfony can't work with it to retrieve roles. Doctrine\Common\Collections\ArrayCollection#000000002300d10300000000545301a6
And using simple_array or json as type doesn't return an object so both are no-go's
Does anyone have a workaround or solution for this? I'm really starting to regret switching from MySQL to PostgreSQL
NOTE: the code was working fine in MySQL, I just switched databases.
You can always create your own mapping-type specially for ArrayCollection properties. The Doctrine DBAL docs and ORM cookbook describe how to create one. The Symfony documentation describes how to activate/register it.
You could for example extract the array and json-encode it when converting to a DB value:
json_encode($value->toArray())
And json-decode and wrap it back into an ArrayCollection when converting to a PHP value:
new ArrayCollection(json_decode($value))
But it's your mapping type, you can do whatever you want :)
I also had this problem myself, I'm not sure exactly why postgresql truncates the serialized string, even tho the field is marked as text. My suggestion is that you convert the column type to json_array because postgresql has a native type for that. Else you can also use custom types which
Related
I am using Money/Money with Symfony and doctrine, but I am not sure how I should perform the mapping. Currently I use the following, resulting in this error message;
Error:
Money\Money could not be converted to string
Mapping:
/**
* #Groups({"group1"})
* #ORM\Column(type="string", nullable=true)
* #var Money
*/
private $price;
Internally Money/Money uses String as representation, thus I thought I could use it as well.
This happens because of doctrine type conversion. Before persisting, doctrine takes the values of your properties and transform them to SQL values using the type you specify in the column annotation. Since the type you are using for your price property is string, doctrine is trying to cast your Money object into a string.
You have many options to fix this, some of them simple and others not that much.
Simple but not optimal: Create a new Money class that will extend from the original one, and create __toString method. This will solve your problem persisting, but the property you'll get from the db will be a string, not an object. If you want to improve that, put some custom logic in your setter so you can create an instance of money from that value. Simple, but dirty.
Medium complex, but might not be what you need: You need to use a doctrine custom type. Is really not that hard. People get scared of this, but jump into the docs and you'll see how simple it is. It consists of basically creating a type, like "money" that contains instructions for doctrine on what to do before persisting properties of that type, and what to do after the value is fetched from the db. So you'll save it as a string in your database still, but you will also control the fetching, being able to create the Money instance. Now, depending on the structure of your money class, this might not be what you need.
Probably your best take: I imagine that your money class not only has the actual value, but maybe a currency type property. If that's the case, probably a Doctrine Embeddable is your best solution. With that you will be able to kinda separate the actual value and the currency code as separate fields in the database. Map your money class to be a Doctrine Embeddable (since is a class that lives outside of your domain logic, in vendor, you will have to use yaml or xml, and not annotations). When the object is fetched from the db, it will be an instance of the Money class. But for that to happen, you need to have proper getters and setters that the property access component can use. Best thing about embeddables, is that they are reusable with other entities.
Hope this info helps! Happy coding!
I am new to doctrine, In my symfony project all the entity annotation linked with groups but I am not getting what is the use of Groups.
/**
* #ORM\Column(type="string", length=64)
* #Groups({"public","details"})
*/
I have newly added the field in my entity file, while executing the schema update getting (newly added field) was never imported.
What is groups and how it's useful.
Groups is not part of doctrine, and is not necessary unless you need serialize objects to use json or xml (Rest API, etc)
http://symfony.com/doc/current/components/serializer.html#attributes-groups
http://symfony.com/blog/new-in-symfony-2-7-serialization-groups
It allows to serialize just selected group of attributes (eg. to skip internal ones).
Read more here: http://symfony.com/blog/new-in-symfony-2-7-serialization-groups
I am using DDD now for quiet some time, it took some time to get used to and separate everything. But am now stuck at updating my entity ...
Setup
Now I have a repo interface that defines the following methods
/**
* #param AccountEntity $account
* #return AccountEntity
*/
public function create(AccountEntity $account);
/**
* #param AccountEntity $account
* #return mixed
*/
public function update(AccountEntity $account);
My repo does exactly 0 in the function because it passes it to the mapper, which in turn creates/updates the data. So far so good.
The application service has the method create which accepts an array, the array is validated and if valid it will use an EntityBuilder to create the entity. (Entity requires the data by __construct). If the data is invalid it will throw an exception.
Now my issue is how to handle my update in the application service.
I get an array of data, just like the create, in the update and an id of the entity.
Should I use an hydrator to loop over the array and update the fields in the entity and then throw it to my repo.
cons, I then have an entity hydrator and builder.
pros, it is easy in use
Should I just validate the data and throw the array + id to the repo and let him figure out what to do
cons, just looks wrong
pros, again easy to make
Should I do toArray or something similar on my entity, merge that with the form data & then use the entity builder to build the entity again and throw the entity then to the repo
cons, don't think an entity should have a toArray function
pros, sounds like it encapsulates the task of building the entity in one place so that seems right.
?
So in short, how to convert an array with data to an entity, or how to save it. I am not using Doctrine in any way, and not planning to
First of all, your input data (array) should be validated somewhere at the controller level, to ensure is in the correct format (we're not talking about business rules, just formatting).
Then your entity can have something like this
class MyEntity
{
public function update($data)
{
//update properties, enforce the relevant business rules
//perhaps events are generated
}
}
The controller will probably use a service method to do the updating. The service will ask the repository for the entity, eventually creates the input format the entity expects it (if there is a difference) then calls the update method .
Then you send the entity to the repository which takes care of persisting it. Remember that the Repository is there to save/restore your entities not to change them.
This should probably have been a comment, but that requires 50 rep...
You should have a look at this article about datamappers: http://www.sitepoint.com/integrating-the-data-mappers/
I have been exactly in your situation, and the articles by that writer (Alejandro Gervasio) helped me immensely.
I have a lot of entities that have ManyToOne and OneToMany associations in Symfony2. As all know if you remove a record and it doesnt set a null value on the association in the other tables, things start to go haywire. So, what is the best way in Symfony2 to handle setting the value as null in other tables when a record is removed in Symfony2?
What do I need to set in my entities to ensure it persists across all associations.
First of all you can manually set ON DELETE behavior for the FK by using onDelete property for the joinColumn annotation in your entity:
/**
* #OneToOne(targetEntity="SomeEntity")
* #JoinColumn(name="some_entity_id", referencedColumnName="id", onDelete="cascade")
*/
private $someEntity;
Secondly, you always can implement event listener for the entity removing event. For example:
public function preRemove(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof SomeEntity) {
return;
}
// Here you can do whatever you want before
// entity record is removed from the DB.
}
You can read about the doctrine event system using following links:
Doctrine events (official manual)
How to Register Doctrine Event Listeners and Subscribers (Symfony2 cookbook)
The difference between them is that the first method works on the database layer. In other words, it would work even if you delete record using raw sql query in the console. Second one is more powerfull because you can also run any php code when entity is removed (for example you can send emails, etc.) but it will only be fired if record is removed via doctrine.
I have database with about 100 tables and I'm using Doctrine 2 as my Data Mapper. I successfully generated entities for my tables, however, I noticed that many-to-one relationships didn't generate bidirectionally. Only many-to-one part of the relation generates, the one-to-many does not.
For instance in my Company entity I have
/**
* #var \User
*
* #ManyToOne(targetEntity="User")
* #JoinColumns({
* #JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
but I don't have anything pointing to the Company entity in User.
I am aware that Doctrine doesn't do this OOTB it says so in their documentation but I was wondering if there is a way to get around this limitation.
Writing 300+ relations by hand is a task I don't want to undertake.
Is there perhaps an alternative Data Mapper library for PHP that can solve this for me?
Thanks in advance.
I ended up using Propel because it generated everything wonderfully, albeit I ended up with some very large files (14k LoC).
It seems there simply isn't a PHP ORM that does everything right.