Doctrine attributes namespace - php

trying to understand what is the Doctrine. I using PHPStorm 2016.3.2 and plugin "PHP Annotations".
When i'm create the Model i'm trying to use annotations like this:
use \Doctrine\ORM\Mapping as ORM;
/**
* Class Region
* #package models
* #ORM\Entity()
* #ORM\Table(name="regions")
*/
class Region { ... }
In annotations i'm using not #Entity, i'm using #ORM\Entity() because IDE understanding what is that and making tips for me. But on this way Doctrine didnt see my classes. How i can to resolve this problem? Thanks.

I came across the same issue while creating the Doctrine configuration through the
Setup::createAnnotationMetadataConfiguration()
method. When you set its fifth parameter $useSimpleAnnotationReader to false you can use the name-spaced syntax the PhpStorm extension expects.

Related

Turn off pluralisation for an endpoint in APi Platform

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);
}
}

Symfony: PHP class not found when loading Appbundle in Appkernel.php

For some weird reason my App and ApiBundle Classes are not found during registerbundles()-function of Appkernel.php.
The classes are there and seeem to be correct and I cannot understand what the problem is.
Appkernel-entries:
new Neulich\AppBundle\NeulichAppBundle(),
new Neulich\ApiBundle\NeulichApiBundle(),
Neulich/src/AppBundle/NeulichAppBundle.php:
<?php
namespace Neulich\AppBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Class NeulichAppBundle
*
* #package Neulich\AppBundle
*/
class NeulichAppBundle extends Bundle
{
}
Please give me a hint, I've been trying for a long time now.
Thanks a lot.
Steffen

Eloquent ORM Code Hinting in PhpStorm

So I'm just starting off with Laravel (using v5) and Eloquent. I'm working on getting some basic APIs up and running and noticing that a lot of working methods don't show up in PhpStorm's code hinting
So I have this model:
namespace Project\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model
implements AuthenticatableContract, CanResetPasswordContract {
}
And in one of my controllers I try to do
User::query()->orderBy('id', 'desc');
User::query() creates a Eloquent Builder object and orderBy() behave properly and without error. However, PhpStorm does not show orderBy() (or take(), skip(), and I'm sure others) when I type User::query()-> and gives warnings when I actually do use it.
I am using Laravel IDE Helper which has helped immensely with bringing code hints to the Facades, but not to the models/builders it would seem.
Does anyone have a solution to this?
For future Googlers, and perhaps OP as well if you are still sticking to Laravel.
The laravel-ide-helper package solves this issue for you quite elegantly, with what I believe is a relatively new feature; generated model PHPDocs.
You can generate a separate file for all PHPDocs with this command:
php artisan ide-helper:models
The generated metadata will look something like this for each class:
namespace App {
/**
* App\Post
*
* #property integer $id
* #property integer $author_id
* #property string $title
* #property string $text
* #property \Carbon\Carbon $created_at
* #property \Carbon\Carbon $updated_at
* #property-read \User $author
* #property-read \Illuminate\Database\Eloquent\Collection|\Comment[] $comments
*/
class Post {}
}
This caused issues for me in PHPStorm however, where the software was complaining about multiple class definitions. Luckily an option is readily available for writing directly to the model files:
php artisan ide-helper:models -W
There are a few more options and settings available if you need to tweak the behavior, but this is the gist of it.
Add in model PHPDoc#mixin
/**
* Class News
* #property int $id
* #property string $created_at
* #property string $updated_at
* #mixin \Eloquent
* #package App
*/
class News extends Model
{
}
In PHPStorm works
You can try Laravel plug-in for PhpStorm and you need to specifically activate it in your project settings.
If you're using BarryVHD's Laravel IDE Helper package, run:
php artisan ide-helper:eloquent
This will write /** #mixin \Eloquent */ into the vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php file.
A little late but I recently had the same problem so I thought I would put a note down:
This is because Database\Eloquent\Model.php has a query() function which returns \Illuminate\Database\Eloquent\Builder and the Eloquent\Builder has a line:
use Illuminate\Database\Query\Builder as QueryBuilder;
Then it uses 'magic' __call methods to call to functions in Query\Builder. (look for __call method in Eloquent\Builder)
See:
http://php.net/manual/en/language.oop5.overloading.php#object.call
__call() is triggered when invoking inaccessible methods in an object context.
So, indeed the method you are calling is inaccessible :) There is not much that the IDE can do.
There are workarounds like using #method tags but it is unmaintainable. An alternative is to use #mixin (but this is not standards based).
See: https://github.com/laravel/framework/issues/7558
I think this all be resolved when they get rid of all the magic calls in the Laravel code and use PHP 'traits' instead. See last message here. :)
Just import Eloquent Builder in your Model class and add mixin:
use Illuminate\Database\Eloquent\Builder;
/** #mixin Builder */
To cover all the models at once — add the mixin to the src/Illuminate/Database/Eloquent/Model.php)
I wanted to have some kind of explicit "casting" when interacting with the query builder. Example...
$user = User::query()->findOrFail($id);
$user->myUserSpecialMethod(); // <-- IDE syntax error
Since all my models are extending my custom base Model which in turn extends Eloquent, I've ended up creating this method in my custom base model:
/**
* Explicit type-hinting
*
* #return static
*/
static public function hint(BaseModel $model)
{
return $model;
}
This way, it solves the IDE invalid error and helps me:
$user = User::hint(User::query()->findOrFail($id));
$user->myUserSpecialMethod(); // <-- all OK !
Please note that this is not OOP type casting. It is only a hint to help the IDE. In my example, the returned Model was already a User. If I woud use this method on a derived class like SuperUser, only the IDE will be fooled...
An nice alternative also is to put meta information directly over the assignment statement:
/** #var User $user */
$user = User::query()->findOrFail($id);
$user->myUserSpecialMethod(); // <-- all OK !
Or next to it...
$user = User::query()->findOrFail($id); /** #var User $user */
$user->myUserSpecialMethod(); // <-- all OK !
Verified on Laravel 8, just added #mixin Builder to Illuminate\Database\Eloquent\Model.php annotation solved it.
// Illuminate\Database\Eloquent\Model.php
/**
* #mixin Builder
*/
abstract class Model

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.

Doctrine 2 - Access level problems when using Class Table Inheritance

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.

Categories