Annotation loading of Doctrine - doctrine-orm

I have defined an entity
use Doctrine\ORM\Mapping as ORM;
/**
* UmMemberSectionInfo
*
* #Table(name="um_member_section_info")
* #Annotations\CharDependent(reflect="TbContentcharset")
* #Entity
*/
class UmMemberSectionInfo extends \DoctrineHelper
{
//some code
}
And an annotation
namespace Annotations;
use Doctrine\Common\Annotations\Annotation;
/**
* #Annotation
* #Target("CLASS")
*/
final class CharDependent extends Annotation{
public $reflect;
}
And read it as
$reader = new AnnotationReader();
$reflectionObj = new \ReflectionObject(new $entity);
$annot = $reader->getClassAnnotation($reflectionObj, '\\Annotations\\CharDependent');
var_dump($annot->reflect);
if ($annot instanceof \Annotations\CharDependent) {}
However, it just pom an error says, The annotation "#Table" in class UmMemberSectionInfo was never imported. which will not be shown when I let the CharDependent annotation off and use it as original. What is the problem? Why it's fine when I use the original?

You must use full annotation name with namespace:
#Doctrine\ORM\Mapping\Table
but you imported use Doctrine\ORM\Mapping as ORM;, so you can use that alias:
#ORM\Table
If you want to use short #Table, you must import Table class:
use Doctrine\ORM\Mapping\Table;
You must do the same for #Entity.

Related

Doctrine prevent error on OneToOne association where record doesn't exist in database

Problem
I'm trying to create a OneToOne association in a Laravel app using Doctrine. When trying to access the association I'm getting this error.
Entity of type 'Status' for IDs clientId(1) was not found
Versions:
Doctrine: 2.7.5
Laravel: 7.30.4
Code:
Client Class
<?php
namespace App\Client;
use App\Person\Person;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Client
* #package App\Client
*
* #ORM\Entity(repositoryClass="ClientRepository")
* #ORM\Table(name="client")
*/
class Client extends Person
{
/**
* #var Status
*
* #ORM\OneToOne(targetEntity="Status", mappedBy="client")
* #ORM\JoinColumn(name="id", referencedColumnName="client_id", nullable=true)
*/
protected $status;
/**
* #return Status
*/
public function getStatus()
{
return $this->status;
}
}
Status Class:
<?php
namespace App\Client\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Status
* #package App\Client\Entity
*
* #ORM\Entity(readOnly=true)
* #ORM\Table(name="status_view")
*/
class Status
{
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(name="client_id", type="integer")
*/
protected $clientId;
/**
* #var \App\Client\Client
*
* #ORM\OneToOne(targetEntity="App\Client\Client", inversedBy="staus")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id", nullable=true)
*/
protected $client;
/**
* #var string
*
* #ORM\Column(name="status", type="string")
*/
protected $status;
/**
* #return string
*/
public function getStatus()
{
return $this->status;
}
}
Calling Code
$client->getStatus()->getStatus()
What I've tried/Answers I've looked at
Entity of type 'AppBundle\Entity\User' for IDs id(155) was not found - I'm not using a Doctrine filter, nor DQL.
https://stackoverflow.com/a/49416542/9530790 - This works, with a few tweaks, by swallowing up the exception, but it feels more like a hack when the docs say nullable should work.
https://stackoverflow.com/a/21887344/9530790 - This states nullable should work but it doesn't.
https://stackoverflow.com/a/15744449/9530790 - Same question different ans. States that Doctrine doesn't support zero-to-one associations, but nullable I believe should be what solves that, but for my problem it's not working. Also there's no link to the docs stating where Zero to one is not supported.
I believe that adding fetch="EAGER" should fix the null issue as elsewhere in our app that works, but when I add that I get an different Doctrine error spl_object_hash() expects parameter 1 to be object, null given, which again has to do with the association not existing.
"Well why aren't you experiencing the above error with your other associations". Great question! After a deep underwater excursion into the Doctrine code, I believe the reason is because those associations are nested and for some reason (I'm not sure why), when nested, the spl_object_hash function, in class UnitOfWork is not called.
Additional Notes:
This is what the object looks like when calling $client->getStatus(), before it errors on the next ->getStatus() call.
DoctrineProxies\__CG__\App\Client\Entity\Status {#2011
+__isInitialized__: false
#clientId: 4
#client: null
#status: null
…2
}
You can see it's a Client Proxy object that's created not a 'true' object, this is why it errors (with Entity of type 'Status' for IDs clientId(1) was not found) when not using fetch="EAGER", since eager loads a true object. See here
This code below in the Proxy object is the what causes the above error. Which is why I can't do a try catch in the parent ('true' Client class), since it errors before calling the parent.
/**
* {#inheritDoc}
*/
public function getStatus()
{
$this->__initializer__ && $this->__initializer__->__invoke($this, 'getStatus', []);
return parent::getStatus();
}
Question:
Why is nullable=true not working as expected, and what should/can I do to make it work?

Symfony - RuntimeException: Unknown key "class" for annotation "#FOS\RestBundle\Controller\Annotations\View"

I want to create web service rest with symfony2,
I'm installed SerializerBundle & FOSRestBundle
when i trying the commande php app/console router:debug
for this purpose but I am getting the following error.
[RuntimeException]
Unknown key "class" for annotation "#FOS\RestBundle\Controller\Annotations\View".
The class View exist.
<?php
namespace Sdz\AdminBundle\Controller;
use FOS\RestBundle\Controller\Annotations\View;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sdz\VoyageBundle\Entity\Promo;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class RestpromoAction extends Controller
{
/**
* #View("restpromo",class="SdzVoyageBundle:Promo")
* #ParamConverter()
*/
public function getPromorestAction(Promo $entitysPromo)
{
return array('restpromo'=>$entitysPromo);
}
}
How can I resolve this issue?
You have added the "class" parameter to #View instead of #ParamConverter.
This is the faulty line:
#View("restpromo",class="SdzVoyageBundle:Promo")
Add the variable-name and corresponding class to your #ParamConverter to resolve your issue.
use FOS\RestBundle\Controller\Annotations as FOSRest;
use Sensio\Bundle\FrameworkExtraBundle\Configuration as SensioFrameworkExtra;
/**
* #FOSRest\View()
* #SensioFrameworkExtra\ParamConverter(
* "promo",
* class="SdzVoyageBundle:Promo"
* )
*/
public function getPromorestAction(Promo $promo)
{
return array(
'restpromo' => $promo
);
}

How do I order by a property that isn't a DB column using Doctrine?

When defining a relationship, there is a property on the related model (not a DB column), but I would like to sort by it (in the #OrderBy annotation).
I have a base model that is extended using single table inheritance. The property in question is basically an order property that is specified in each child class, but is not saved to the DB.
(I don't want to add an order column to the DB table, since the ordering depends purely on which child class the discriminator is mapped to. There is already a unique constraint so that each child class can be used no more than once in the relationship.)
Here's a really simplified version of my code...
Base entity:
/**
* #ORM\Entity
* #ORM\Table(name="base")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="class_name", type="string")
*
* #ORM\DiscriminatorMap({
* "Base" = "Models\Base",
* "ChildA" = "Models\ChildB",
* "ChildB" = "Models\ChildA"
* })
**/
class Base
{
/** #ORM\Column(type="string") **/
protected $class_name;
/**
* #ORM\ManyToOne(targetEntity="Related", inversedBy="collection")
**/
protected $related;
// this is just a plain ol' property (not in the DB)
protected $order;
public function getClassName()
{
return $this->class_name;
}
}
Children:
/**
* #ORM\Entity
* #ORM\Table(name="child_a")
**/
class ChildA extends Base
{
$order = 1;
}
/**
* #ORM\Entity
* #ORM\Table(name="child_b")
**/
class ChildB extends Base
{
$order = 2;
}
Related entity:
/**
* #ORM\Entity
* #ORM\Table(name="related")
**/
class Related
{
/**
* #ORM\OneToMany(targetEntity="Base", mappedBy="related")
* #ORM\OrderBy({"order"="ASC"})
**/
protected $collection;
public function getCollection()
{
$em = App::make('Doctrine\ORM\EntityManagerInterface');
// map each Base instance to the appropriate child class
return $this->collection->map(function ($base) use ($em) {
$class_name = $base->getClassName();
return $em->find($class_name, $base->getId());
});
}
}
Is it possible to use the order property for ordering the collection relationship? (Ordering based on class_name using a switch-like construct would also be valid, but I haven't found any way to do that either, and it would be harder to maintain.)
Thanks in advance!
The directive beginning with ORM is very much telling Doctrine you're doing referencing a property that has a relationship with a table field. You can't use ORM directives on fields that don't exist. Doctrine annotations: OrderBy
You would have to implement this in a function, best in the model itself (within your getCollection() function), or if you're using a framework like Symfony place it in a function of the repository class for this entity. You'd have to use PHP sorting functions to do this. SQL/DQL won't work either because the property isn't related to a field in the table.

Dynamic relationship and the SonataAdminBundle

I use the SonataAdminBundle for the first time in my life and I have some
problems with it.
At first, I had a PageBundle which has a Page and a Author entity. Then I
started using SonataAdminBundle and used the sonata_type_model to nicely
display the Authors of a Page:
// ...
protected function configureFormFields(FormMapper $mapper)
{
$mapper
->add('title')
->add('slug', null, array('required' => false))
->add('published', null, array(
'label' => 'publish',
'required' => false,
))
->add('author', 'sonata_type_model')
->add('content')
;
}
But then I discovered the SonataUserBundle. I started using it and when I
finally get it working I thought it would be nice to make use of this User
entity, instead of the Author entity in the PageBundle. To make this possible,
I used the technique where the documentation talks about in
"How to Define Relationships with Abstract Classes and Interfaces".
This worked, but not with the SonataAdminBundle. It seems like the
sonata_type_model doesn't work with the ResolveTargetEntityListener and I
can't make it working.
The relevant Page entity code in the PageBundle:
namespace Wj\PageBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table
* #ORM\Entity
* #ORM\HasLifeCycleCallbacks
*/
class Page
{
/**
* #var integer $authorId
*
* #ORM\ManyToOne(targetEntity="Wj\PageBundle\Model\AuthorInterface")
*/
private $author;
}
The Wj\PageBundle\Model\AuthorInterface:
namespace Wj\PageBundle\Model;
interface AuthorInterface
{
public function getId();
}
And the User entity in the UserBundle:
namespace Wj\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser;
use Wj\PageBundle\Model\AuthorInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser implements AuthorInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __toString()
{
return $this->getFirstName().' '.$this->getLastName();
}
}
The FormMapper configuration is the same as I posted above.
How can I use the ResolveTargetEntityListener technique in combination with
the SonataAdminBundle and his sonata_type_model?

How can I use a different namespace class in Doctrine2 targetEntity mapping

When I set a ManytoOne mapping, while both class in same namespace, it works.
but it won't work if the two class are in different namespace?
/**
* #ORM\ManyToOne(targetEntity="OP\ProjectBundle\Entity\Project", inversedBy="tickets")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
protected $project;
You have to use the absolute namespace of your target entity - note the leading backspace in its name.
/**
* #ORM\ManyToOne(targetEntity="\OP\ProjectBundle\Entity\Project", inversedBy="tickets")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
protected $project;