Symfony doctrine lazy load properties - php

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.

Related

Is there a way to handle duplicate enteries?

I am using the api-platform framework with a MySQL backend. I am getting errors when API clients use the POST end point to submit data if there is already an entry in the database.
Currently I am using a PRE_WRITE EventSubscriberInterface class to find the original database entry and delete it. However this seems incredable inefficient compared to a simple update action.
I am able to update the existing database entry, but then I'm unable to remove/stop the POST'd an item from being executed.
Is there a way around this? Ether to change the INSERT action to an ...ON DUPLICATE... or to simple stop the data the user post'd from being saved to the database?
You can also use the property "UniqueEntity" to set what's make your entity unique.
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity("slug")
*/
class Article
{
//...
/**
* #ORM\Column(type="string", length=255)
*/
private $slug;
}

Symfony Doctrine ORM not a valid entity or mapped super class

I'm just making a new Entity as usual, but something goes wrong and console report this error and I couldn't generate the entity setter/getter:
[Doctrine\ORM\Mapping\MappingException]
Class "AppBundle\Entity\Admin_Actions" is not a valid entity or mapped super class.
Here is my Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="admin_actions")
*/
class Admin_Actions
{
/**
* #ORM\Column(name="id",type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="uid",type="string",length=100)
*/
private $uid;
/**
* #ORM\Column(name="type",type="integer")
*/
private $type;
/**
* #ORM\Column(name="linedate",type="datetime")
*/
private $linedate;
}
If I do doctrine:mapping:info:
[Exception]
You do not have any mapped Doctrine ORM entities according to the current configuration. If you have
entities or mapping files you should check your mapping configuration for errors.
I've just waste an hour trying to investigate the problem and I've already tried to rewrite it from new but I'm missing something. What's wrong with this?
May be datetime field has same name as function/implementation in doctrine, I have got same mistake by naming a table "condition" which may be condition function in MySql query

Doctrine2 - Annotation - "findOneBy" on column in "OneToOne" joined table

Using Doctrine2, I am trying to execute a findOneBy on an entity, where I have a joined a table using OneToOne, and I want to search for columns in the joined table.
The two PHP entities in play are (in simplified versions):
Page:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="Page")
* #ORM\HasLifecycleCallbacks()
*/
class Page extends EntityInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="boolean")
*/
public $isActive;
/**
* #ORM\OneToOne(targetEntity="\PageLocalization")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id", referencedColumnName="pageId")
* })
**/
public $pageLocalization;
}
PageLocalization:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="PageLocalization")
* #ORM\HasLifecycleCallbacks()
*/
class PageLocalization extends EntityInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
public $pageId;
/**
* #ORM\Column(type="string")
*/
public $localeCode;
/**
* #ORM\Column(type="string")
*/
public $title;
}
The entities work and I can extract data just fine through e.g. $entityRepository->findOneBy(["id"=>1]).
Now, for instance, I want to search for Page.id = 1, Page.isActive = true and PageLocalization.localeCode = "en-US". How is this search performed?
My attempt below doesn't work.
$entityRepository->findOneBy([
"id" => 1,
"isActive" => true,
"pageLocalization" => [
"localeCode" => "en-US"
]
]);
I had no luck finding the answer through Google, Stackoverflow, or the Doctrine2 documentation.
The most frequent solutions I see is that people settle for are constructing the queries manually using $entityManager->createQueryBuilder(). However, I feel this approach defies the purpose of having the entities.
Is this type of search possible at all using purely annotations and entities?
From what I've gathered thus far, it is indeed not possible to perform a search like this across multiple entities (joined tables) using any of the EntityRepository's "find" methods; find, findAll, findBy, and findOneBy.
Stackoverflow answers to similar questions:
https://stackoverflow.com/a/16730168/1879194
https://stackoverflow.com/a/12258270/1879194
https://stackoverflow.com/a/19489147/1879194
The list goes on...
Conclusion
One must perform this type of searches using DQL or the QueryBuilder.
Thoughts
I find this limitation to be highly obnoxious and a serious feature inadequacy in Doctrine2.
If half the time, when I want to touch the database, I have to build the queries myself, why would I bother adhering to and maintaining two drastically different approaches; (1) an ORM with entities reflecting database tables and (2) constructing pure SQL requests through a DBAL, e.g. PDO or Doctrine's DQL?
Doctrine2 considerations (slightly off-topic)
I really want to learn how to work with and utilize Doctrine2 efficiently. And while Doctrine2 does have a grand array of nice features, it certainly also consists of some massive rebar walls, which one keeps running into face-first. Google and Stackoverflow certainly has a bunch of "Why can't I ...?" questions related to Doctrine2 and joining tables in particular.

Class inheritance is not found

I am building an application using Zend Framework and Doctrine 2.
My code looks like this:
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #Entity (repositoryClass="Repositories\Person")
* #Table(name="persons")
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="type", type="string")
* #DiscriminatorMap({"2"="User"})
*/
class Person
{
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
}
And my class User
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #Entity (repositoryClass="Repositories\User")
*/
class User extends Person
{
}
Now, I get this error:
Fatal error: Class 'Entities\Person' not found in C:\xamp\htdocs\Persons\application\m
odels\Entities\User.php on line 13
I have no idea why I get that error. I have tried calling the "Person" class in many different ways but the its not working. Any idea? Thanks!
When running in Zend Framework, you have an autoloader setup that handles the loading of classes for you dynamically.
When you run just the Doctrine tool from the command line, you don't have an autoloader at your disposal. Doctrine is trying to load the User class, which requires Person, and yet it doesn't (apparently) know how to load Person.
I think the simple solution would be to have require_once('Person.php'); at the top of your User entity. This is probably unnecessary for ZF, but will be helpful for Doctrine command line tools.

Symfony 2 + Doctrine 2 + inheritance

I'm searching for a solution for the following problem with a database inheritance using Doctrine 2 built in Symfony 2 framework. This is what I want to do...
I want to create two tables (UredniHodiny, KonzultacniHodiny) with the same interface as the abstract class Hodiny. This is how I'm trying to do it
<?php
// src/CvutPWT/ImportBundle/Entity/Hodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*/
abstract class Hodiny
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Osoba")
*/
protected $osoba;
/**
* #ORM\ManyToOne(targetEntity="Mistnost")
*/
protected $mistnost;
/**
* #ORM\Column(type="datetime")
*/
protected $zacatek;
/**
* #ORM\Column(type="datetime")
*/
protected $konec;
}
<?php
// src/CvutPWT/ImportBundle/Entity/KonzultacniHodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="konzultacnihodiny")
*/
class KonzultacniHodiny extends Hodiny
{
}
<?php
// src/CvutPWT/ImportBundle/Entity/UredniHodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="urednihodiny")
*/
class UredniHodiny extends Hodiny
{
}
Now when I run php app/console doctrine:generate:entities CvutPWTImportBundle Symfony generates all variables (more precisely columns) from class Hodiny as private variables to both child classes. Now when I'm trying to create those tables with app/console doctrine:schema:update --force I'm getting errors that $id must be protected or weaker. When I change this protection manually I am able to create tables but there is only one column (id). But this is not what I was hoping for. Can somebody give me any advice what I'm doing wrong?
This is not table inheritance. Mapped super classes are just mapping inheritance.
The tables corresponding to your final entities will not be relied together in any way.
If you want real table inheritance (single table or joined table), use this: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
If you still want to use mapped super classes, then you will have to put the #ORM\Id definition in both final classes. You can not put ids in mapped super classes.

Categories