as part of the development of my CMS that I publish in a while .. I am facing a problem.
error :
[Semantical Error] The annotation "#Doctrine\ORM\Mapping\Entity" in class ScyLabs\GiftCodeBundle\Entity\GiftCode does not exist, or could not be auto-loaded.
I explain to you ,
Basically, in the project, everything is Overridable, it is already the case, with configurations in the file services.yaml.
For obvious reasons of simplicity, and an immediate need, to allow me to create a second bundle inheriting from it. I told myself that doing my "Override" or saying to the project: "Hello here I am, I am a class uses me" is very convenient with annotations (and much clearer).
So, I create a custom annotation (So far so good ..) That you find here ..
<?php
/**
* Created by PhpStorm.
* User: alex
* Date: 04/11/2019
* Time: 14:25
*/
namespace ScyLabs\NeptuneBundle\Annotation\ScyLabsNeptune;
/**
* #Annotation
* #Target({"CLASS"})
* #Attributes({
* #Attribute("key",type="string"),
* #Attribute("classNameSpace",type="string"),
* })
*/
class Override
{
/**
* #var string
*/
public $key;
/**
* #var string
*/
public $classNameSpace;
public function __construct(array $opts) {
$this->key = $opts['value'];
$this->classNameSpace = $opts['class'];
}
}
Well, my annotation was in place, I will now put it in an entity, .. As here
<?php
/**
* Created by PhpStorm.
* User: alex
* Date: 05/11/2019
* Time: 10:20
*/
namespace ScyLabs\GiftCodeBundle\Entity;
use ScyLabs\NeptuneBundle\Annotation\ScyLabsNeptune;
use Doctrine\ORM\Mapping as ORM;
/**
* #ScyLabsNeptune\Override("gift",class="ScyLabs\GiftCodeBundle\Entity\GiftCode")
* #ORM\Entity()
*/
class GiftCode
{
}
Why do that ? And in fact, everything is automated in the neptune, except special case, it will automatically generate all the URLs necessary for the proper functioning of an entity (ADD / EDIT / DELETE / LIST) ... And for this, it must indicate to the project that the entity exists, and that it must be part of this system.
So, until now I use a very complete configuration in services.yaml, in which I fill a table keys => value, corresponding to "key" => "Namespace"
In my case: "gift" => "ScyLabs \ GiftCodeBundle \ Entity \ GiftCode"
In short, suddenly, for override, I do a treatment in a compilation step
<?php
/**
* Created by PhpStorm.
* User: alex
* Date: 01/08/2018
* Time: 09:46
*/
namespace ScyLabs\NeptuneBundle;
class ScyLabsNeptuneBundle extends Bundle
{
public function getContainerExtension()
{
// Compilation de l'extension
return new ScyLabsNeptuneExtension();
}
}
And in this extension, I have this piece of code that makes everything
$bundles = require $projectDir.'/config/bundles.php';
foreach ($bundles as $bundle => $env){
if($bundle === ScyLabsNeptuneBundle::class)
continue;
if(method_exists(new $bundle,'getParent') && (new $bundle)->getParent() === ScyLabsNeptuneBundle::class){
$reader = new AnnotationReader();
$reflClass = new \ReflectionClass(GiftCode::class);
$classAnotations = $reader->getClassAnnotation($reflClass,"Override");
foreach ($classAnotations as $classAnotation){
if($classAnotation instanceof Override && class_exists($classAnotation->classNameSpace)){
$config['override'][$classAnotation->key] = $classAnotation->classNameSpace; }
}
}
}
From what I suspect after a lot of research, at the compilation stage of my extension, #ORM \ Entity, and or / / Autowire, it seems not compiled yet.
The problem is that suddenly, when I get my personal annotation (Override), I can not recover #ORM \ Entity, and I can not necessarily remove it because it would not work anymore as an entity.
Why do that here? Because behind I have another step of comoilation (A CompilationPass)
$container->addCompilerPass(new ResolveDoctrineTargetEntitiesPass(),PassConfig::TYPE_BEFORE_OPTIMIZATION,1000);
Who, redefined the Entities that doctrine will call in relation to the painting that I send to him (you know, the one I defined just before).
With this I give the possibility of override entities with an identical name.
What to do ?? .. I confess that I can not do more ...
Thanks in advance friends;)
By default, the annotation reader does not use the same autoloader as classes.
You need to tell him how to load the annotation class like that :
AnnotationRegistry::registerUniqueLoader('class_exists');
For more explanation, you can look at the doc https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/annotations.html#registering-annotations
thanks for you response.
But it don't work and this fonction is deprecated and removed to Annotations 2.0.
BUT , when i try i found a resolution.
When i follow your link and try the code in the page , i try this function
AnnotationRegistry#registerFile($file)
For get #ORM\Entity file path , i use
new \ReflectionClass(ORM\Entity::class);
And , this work.
I deleted AnnotationRegistry#registerFile($file) function , and this work.
Thanks you for help ;)
You'r the best
Related
In a Symfony 5 project we're using the APi Platform to generate a REST API.
One of the entity classes is called FarmMetadata.
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Table(... some settings ...)
* #ORM\Entity
*/
class FarmMetadata
{
// properties and methods
}
When I run php bin/console debug:router it shows the following routes for this resource:
api_farm_metadatas_get_collection GET ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_post_collection POST ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_get_item GET ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_delete_item DELETE ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_put_item PUT ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_patch_item PATCH ANY ANY /api/farm_metadatas/{id}.{_format}
However the word "metadata" is already plural. There's no such thing as metadatas. How can I turn off the pluralisation for this endpoint?
I tried using shortName:
* #ApiResource(
* shortName="FarmMetadata" // also "farm_metadata"
* )
but it doesn't change the output.
If I use:
* #ApiResource(
* shortName="Metadata"
* )
then the route names and paths are changed:
api_metadata_get_collection GET ANY ANY /api/metadata.{_format}
api_metadata_post_collection POST ANY ANY /api/metadata.{_format}
api_metadata_get_item GET ANY ANY /api/metadata/{id}.{_format}
api_metadata_delete_item DELETE ANY ANY /api/metadata/{id}.{_format}
api_metadata_put_item PUT ANY ANY /api/metadata/{id}.{_format}
api_metadata_patch_item PATCH ANY ANY /api/metadata/{id}.{_format}
but that's not what I want.
I know that I can declare a path for every operation, but that would hurt the DRY principle.
How can I achieve the desired behaviour?
You could use "path" option on each operation.
Cf https://api-platform.com/docs/core/operations/#configuring-operations
For example
* shortName="Metadata",
* itemOperations={
* "get"={
* "path"="/metadata/{id}"
I don't think this is possible by configuration: these routes are built in the private method ApiPlatform\Core\Bridge\Symfony\Routing\ApiLoader::addRoute (at least in v2.6 which I'm using), and this uses a static call to a pluralizer - so: decorating the ApiLoader is not easily possible (as the addRoute method is private), and exchanging the ways of generating the route is not possible (due to the usage of a static method call).
Looks like you need to open a feature request ticket in their bug tracker...
you can do easily as you want
api_platform:
...
path_segment_name_generator: App\InfraStructure\ApiPlatform\Core\SingularPathSegmentNameGenerator
create SingularPathSegmentNameGenerator
<?php
declare(strict_types=1);
namespace App\InfraStructure\ApiPlatform\Core;
use ApiPlatform\Core\Operation\PathSegmentNameGeneratorInterface;
use ApiPlatform\Core\Util\Inflector;
final class SingularPathSegmentNameGenerator implements PathSegmentNameGeneratorInterface
{
public function getSegmentName(string $name, bool $collection = true): string
{
return Inflector::tableize($name);
}
}
I have installed the FOSUserBundle and installed it as per its detailed installation guide (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md).
When i run php console doctrine:schema:update --force for the first time, it populates the users table with all of the default fields that the FOSUserBundle has defined.
Unfortunately it appears to be completely missing the fields which i have added to my user entity and i am wondering if its utilising the configuration file which is specified in the installation guide instead of using the annotations which are in the entity.
It also appears to be ignoring the other entities within the same Bundle.
namespace Acme\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Bureau", mappedBy="id")
*/
protected $bureau;
public function __construct() {
parent::__construct();
}
}
This is my user entity, of which bureau is being completely ignored regardless if it has a relationship or not.
Edit
As per requested, please find below the orm config file. It's the default file as per the configuration.
I have suspected this to be the problem, but i wasnt sure if annotations and the config file could work together.
Acme\UserBundle\Entity\User:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: AUTO
Edit 2
I have found that if i remove the orm configuration file that it all magically works again!!
So i would adjust my question for clarity.
Updated question
If an orm configuration file exists, are annotations ignored?
When you generate entities with the console, you are asked on the format, which is:
xml
yaml
annotations
Regardless of what you choose, there are no signifiers telling Doctrine which to use besides the fact one exists. In order, YAML takes priority over annotations, and so it should.
I spent hours banging my head against the wall with this. The labels for my form fields didn't appear no matter what.
Finally found that without the extra space where the cursor is (see image), all annotations get ignored. I'm using ZF 2.1.1 with Doctrine Common 2.2.3.
Am I doing something wrong? Or is this a bug in ZF or the Doctrine parser?
Works:
class LoginForm
{
/** #Annotation\Type("text")
* #Annotation\Options({"label":"Store ID:"})
* #Annotation\Required(true)
* #Annotation\Filter({"name":"StringTrim"})
* #Annotation\Validator({"name":"StringLength","options":{"min":2,"max":64}})
*/
public $sStoreId;
}
Fails, unless there is a space after /**:
class LoginForm
{
/**
* #Annotation\Type("text")
* #Annotation\Options({"label":"Store ID:"})
* #Annotation\Required(true)
* #Annotation\Filter({"name":"StringTrim"})
* #Annotation\Validator({"name":"StringLength","options":{"min":2,"max":64}})
*/
public $sStoreId;
}
There seems to be no solution so use one of the workarounds provided in the original question:
add a space after /** (easy to forget)
put the first annotation or any text comment in the same line as /**
Because the annotation are using the php-doc standard, the first line is always for a comment/description. It must be given. If you provide no comment/description, leave the line empty.
We're having problems calling a certain custom entity repository function from our controller in a Symfony2 project. We have successfully done it before with other entities so we're probably missing something and I can't figure out what it could be.
Our repository class looks like this:
<?php
namespace OurSite\Bundle\OurBundle\Entity;
use Doctrine\ORM\EntityRepository;
class BlogRepository extends EntityRepository
{
public function findPreviousPosts($limit = 6)
{
$q = $this->createQueryBuilder('q')
->where('q.category = :category')
->setMaxResults($limit)
->add('orderBy', 'q.published ASC')
->getQuery();
$res = $q->getResult();
return $res;
}
}
The entity:
<?php
namespace OurSite\Bundle\OurBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* OurSite\Bundle\OurBundle\Entity\Blog
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="OurSite\Bundle\OurBundle\Entity\BlogRepository")
*/
class Blog {
// Non-relevant stuff here
}
When we call the method like this:
$em = $this->getDoctrine()->getEntityManager();
$previousPosts = $em->getRepository('OurSiteOurBundle:Blog')->findPreviousPosts();
We get this:
Undefined method 'findPreviousPosts'. The method name must start with either findBy or findOneBy!
If we do echo get_class($em->getRepository('OurSiteOurBundle:Blog')); it outputs BlogRepository, as expected.
What could be causing the problem? We have a superfluous bundle directory in the project but I'm guessing that can't be causing it?
From the source you provided, this may not be your issue, but it may save others some search time.
I was coming across the same "must start with either findBy or..." error, and it turns out in my Entity definition I had accidentally made a call to the #ORM\Entity annotation Twice. The first time I used it properly and set the repositoryClass, but the second time I just used it by itself (as with an Entity that wouldn't have a custom repository) and so that overwrote the previous repositoryClass definition.
/**
*
* #ORM\Entity(repositoryClass="Company\TestBundle\Entity\MyEntityRepository")
* #ORM\Table(name="testing_my_entity")
* #ORM\Entity
*/
class MyEntity
{
etc...
}
If you get this error: The method name must start with either findBy or findOneBy! that means that your custom repository isn't loaded.
Check for typos in the code, clear cache, make sure "OurSiteOurBundle" is the actual shortcut name.
I had the same problem. I've seen many posts about that but nothing solved it.
Finally I found out that was because I was previously using generated yml files, so Doctrine didn't read annotations for mapping !
So just be sure you don't have any yml/xml Doctrine files.
And then :
app/console doctrine:cache:clear-metadata
Did you used this entity before? I see strange entity shortcut for Blog
OurSiteOurBundle:Blog
but your Blog have OurSite\ Bundle\OurBundle\Entity namespace. I think it should be
OurSiteBundleOurBundle:Blog
and entity manager points you to wrong repository class
If use xml for mapping(pass tested):
Update xml or yml mapping file, add repository-class attribute:
<entity name="Ccd\Bundle\FrontendBundle\Entity\UvUpdatePageContent" table="uv_update_page_content" **repository-class="Ccd\Bundle\FrontendBundle\Entity\UvUpdatePageContentRepository"**>
http://doctrine-mongodb-odm.readthedocs.org/en/latest/cookbook/mapping-classes-to-orm-and-odm.html
Then update doctrine cache:
php app/console doctrine:cache:clear-metadata
use yml(not tested):
Acme\DemoBundle\Entity\Post:
type: entity
table: posts
RepositoryClass: Acme\DemoBundle\Entity\PostRepository
I'm trying to implent the Class Table Inheritance Doctrine 2 offers in my Symfony 2 project.
Let's say a have a Pizza class, Burito class and a MacAndCheese class which all inherit from a Food class.
The Food class has the following settings:
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="dish", type="string")
* #ORM\DiscriminatorMap({"pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
class Food {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
And the inherited classes have these settings (Pizza for example):
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food_pizza")
*/
class Pizza extends Food {
When running doctrine:schema:update --force from the Symfony 2 app/console I get an error about the access level of $id in the children of Food (Pizza for example), stating it must be protected or weaker. I haven't declared $id anywhere in the Pizza, since I reckoned it would be inherited from Food.
So I tried to declare $id, but that gives me an error, cause I can't redeclare $id.
I figure I need some kind of reference to $id from Food in Pizza, but the Doctrine 2 documentation didn't really give me a clear answer on what this would look like.
Hopefully you understand what I mean and can help me.
Apparently I should have investigated the code generated by doctrine:generate:entities a bit more. When I started my IDE this morning and seeing the code again, I noticed that it had 'copied' all of the inherited fields (like $id in Food, in the example above) to the children (Pizza, in the example above).
For some reason it decided to make these fields private. I manually changed the access level to protected in all of the classes and I tried to run doctrine:schema:update --force again: it worked!
So, as in many cases, the solution was a good night's rest! ;)
If someone comes up with a better solution and / or explanation for this problem, please do post it. I'd be more than happy to change the accepted answer.
Something to keep in mind:
Every Entity must have an identifier/primary key. You cannot generate
entities in an inheritance hierachy currently (beta) As a workaround
while generating methods for new entities, I moved away from project
inheritated entities and after generating I moved them back.
source
May be you should define the #ORM\DiscriminatorMap in a such way:
/**
*
..
* #ORM\DiscriminatorMap({"food" = "Food", "pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
If you compare your code with the example from Doctrine site, you will see that they added parent entity to the DiscriminatorMap.