Eloquent ORM Code Hinting in PhpStorm - php

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

Related

How could this Laravel Model code was generated?

I have an old Laravel app laravel-5.4, I review it recently, and I wondered how did that code was generated for the model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Defect
*
* #property int $id
* #property string $code
* #property string $title
* #property bool $defgroup_id
* #method static \Illuminate\Database\Query\Builder|\App\Defect whereCode($value)
* #method static \Illuminate\Database\Query\Builder|\App\Defect whereDefgroupId($value)
* #method static \Illuminate\Database\Query\Builder|\App\Defect whereId($value)
* #method static \Illuminate\Database\Query\Builder|\App\Defect whereTitle($value)
* #mixin \Eloquent
*/
class Defect extends Model
{//....
I'm pretty sure that I could not able to write all property comments there, but I could not remember how did they had written in the model?!
I tried php artisan make:model Defect but I almost get a plain model without any comments nor relations with about five lines of code.
Could any one able to remember me how could that code with comments was generated?
You can install this composer package:
https://github.com/barryvdh/laravel-ide-helper
Once you have that you can run this, which will generate the doc block similar to the one you listed in your example:
https://github.com/barryvdh/laravel-ide-helper#automatic-phpdocs-for-models
The package a bit more then generate the doc block you mentioned, it helps greatly with IDE code completion, I highly recommend it.

Why is this method available in this trait?

I am looking to extend a trait by using it in another trait. However the trait is using a method that looks like it isn't extending. The trait works, so I am wondering how.
Why does this trait have access to the markEntityForCleanup method?
The code is in this repo for Drupal Test Traits
<?php
namespace weitzman\DrupalTestTraits\Entity;
use Drupal\Tests\node\Traits\NodeCreationTrait as CoreNodeCreationTrait;
/**
* Wraps the node creation trait to track entities for deletion.
*/
trait NodeCreationTrait
{
use CoreNodeCreationTrait {
createNode as coreCreateNode;
}
/**
* Creates a node and marks it for automatic cleanup.
*
* #param array $settings
* #return \Drupal\node\NodeInterface
*/
protected function createNode(array $settings = [])
{
$entity = $this->coreCreateNode($settings);
$this->markEntityForCleanup($entity);
return $entity;
}
}
I found the issue.
When using the Drupal Test Traits package you are expected to use your own custom php-unit bootstrap.php and manually load the required packages.
Adding this line to the bottom of the bootstrap script will gain access to the namespace in php.
// <?php is needed for SO to do the syntax highlighting.
<?php
// Register more namespaces, as needed.
$class_loader->addPsr4('weitzman\DrupalTestTraits\Entity\\', "$root/vendor/weitzman\drupal-test-triats\src\Entity");

Doctrine2 Entity Manager cannot find Custom Repository Class in namespace

I have Silex setup with Doctrine2 ORM. I am trying to build a pagination class that I can use with my entities. I am well aware of the existing pagination classes that exist within Doctrine2 but because this project is for my school research I am trying to create this component myself.
Below is the fatal error I get when accessing this page:
Fatal error: Class 'PlayGround\Model\Helper\UserRepository' not found in D:\web\playground-solutions\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php on line 689
I have defined an interface called PaginateableInterface with two methods count and paginate. I went on to define a custom EntityRepository class that extends Doctrine\ORM\EntityRepository. Below is my custom EntityRepository.
<?php
namespace PlayGround\Service\Doctrine;
use Doctrine\ORM\EntityRepository as ParentEntityRepository;
class EntityRepository extends ParentEntityRepository{
public function count(){
$em = $this->getEntityManager();
$builder = $em->createQueryBuilder();
/**
* ToDo: #entity
*
* Still need to find a better way of getting entity class name.
*/
$entity = $em->getClassMetadata(get_class(__CLASS__))->getName();
//Dynamically get a count of records on any entity we happen to call this on.
$builder->select($builder->expr()->count('e'))
->from($entity, 'e');
$query = $builder->getQuery();
//Try-Catch block ommitted
return $query->getSingleScalarResult();
}
}
<?php
namespace PlayGround\Model\Helper;
use PlayGround\Service\Doctrine\EntityRepository as CustomRepository;
use PlayGround\Contract\PaginateableInterface as IPaginate;
class UserRepository extends CustomRepository implements IPaginate
{
}
In my understanding this should suffice as the count and paginate methods are sitting within the custom repository.
Inside my Paginator class I call the entity I want to paginate as shown below:
<?php
//Paginator class
$model = $this->getModel($model);
//Count should be inherited from CustomRepository aliased object.
$totalRecords = $model->count();
Below is another pierce of meet with regards to this where I add an annotation to my model to point it to the repository class it is suppose to use.
<?php
namespace Application\Model\Entity;
use Doctrine\ORM\Mapping as ORM;
use Application\Model\Entity\UserGroup;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #ORM\Entity(repositoryClass="PlayGround\Model\Helper\UserRepository")
*/
class User{ /* Rest of the code goes here... */ }
Given all this setup what could I have missed in getting this to work? I have even ran two commands on my doctrine console but that didn't help either.
Luyanda.Siko#ZACT-PC301 MINGW64 /d/web/playground-solutions
$ php app/Console/bin/doctrine.php orm:clear-cache:metadata
Clearing ALL Metadata cache entries
Successfully deleted cache entries.
Luyanda.Siko#ZACT-PC301 MINGW64 /d/web/playground-solutions
$ php app/Console/bin/doctrine.php orm:clear-cache:query
Clearing ALL Query cache entries
Successfully deleted cache entries.
EDIT:
Below is my file structure found in D:\web\playground-solutions.
You declare twice #ORM\Entity. Once with the repositoryClass and once without. Remove the one without:
#ORM\Entity
and leave this:
#ORM\Entity(repositoryClass="PlayGround\Model\Helper\UserRepository")
#ORM\HasLifecycleCallbacks should be declared without parentheses ()...
Also make sure that the EntityRepository is in the correct namespace and the corresponding folder:
your namespace is PlayGround\Model\Helper\UserRepository meaning the file should be in folder PlayGround\Model\Helper and the class file name should be UserRepository.php.
Fix and check that and if it still doesn't work leave a comment.
UPDATE:
Your UserRepository is in the wrong module. Is now in app should be in PlayGround
The file should be in:
src/PlayGround/Model/Helper/UserRepository.php
It's all about that problem.
Clearly this has really consumed my thought process. All I was doing was pointing to an incorrect namespace as pointed out by #Witt.
I changed my annotation entry in the User entity and the error went away.
<?php
/** #ORM\Entity(repositoryClass="Application\Model\Helper\UserRepository") */
Thanks you guys.

Add Class to Laravel 4 Package

I am using this Laravel 4 package for interacting with the Xero accounting application: https://github.com/Daursu/xero
In the GitHub README, it says that you can extend the package easily by using the following code:
namespace Daursu\Xero;
class CreditNote extends BaseModel {
/**
* The name of the primary column.
*
* #var string
*/
protected $primary_column = 'CreditNoteID';
}
I tried adding this as a new Model, but Laravel gives me a Class not found error.
I'm assuming this is a namespacing issue a but can't seem to get it right. I have tried using \Darsu\Xero and also \Darsu\Xero\BaseModel, and other various combinations with and without the initial \.
Any tips on how to do this right?
Easiest way to achieve your intentions:
1) Create a file CreditNote.php in app\models
2) Put the following code in the above file:
use Daursu\Xero\BaseModel;
class CreditNote extends BaseModel {
/**
* The name of the primary column.
*
* #var string
*/
protected $primary_column = 'CreditNoteID';
}
3) Whenever you need to use the CreditNote model, use $creditNote = new CreditNote();

Preserving auto-completion abilities with Symfony2 Dependency Injection

I'm using PHP Storm as my IDE, but I believe that other IDE's such as Netbeans will have the same issue as I'll explain below.
When using a framework like Symfony2, we have the wonderful world of Dependency Injection added. So objects can simply be instantiated using code like the following snippet:
$myThingy = $this->get('some_cool_service');
This is very handy, as objects are already configured beforehand. The one problem is, that auto-completion breaks entirely in basically any PHP IDE, as the IDE does not know what type the get() method is returning.
Is there a way to preserve auto-completion? Would creating for example an extension of Controller be the answer? For example:
class MyController extends Controller {
/**
* #return \MyNamespace\CoolService
*/
public getSomeCoolService() {
return new CoolService();
}
}
and then for application controllers, specify MyController as the base class instead of Controller?
What about using a Factory class, or any other possible methods?
It is more involving, but you can still do this with eclipse PDT:
$myThingy = $this->get('some_cool_service');
/* #var $myThingy \MyNamespace\CoolService */
UPDATE:
The example on this page shows you may also use the other way round with phpStorm:
$myThingy = $this->get('some_cool_service');
/* #var \MyNamespace\CoolService $myThingy */
You could define private properties in your controllers
class MyController extends Controller
{
/**
* #var \Namespace\To\SomeCoolService;
*/
private $my_service;
public function myAction()
{
$this->my_service = $this->get('some_cool_service');
/**
* enjoy your autocompletion :)
*/
}
}
I use base Controller class for bundle. You need to annotate the return in method. At least that works on Eclipse.
/**
* Gets SomeCoolService
*
* #return \Namespace\To\SomeCoolService
*/
protected function getSomeCoolService()
{
return $this->get('some_cool_service');
}
I don't like /*var ... */, because it gets too much into code.
I don't like private properties, because you can wrongly assume that services are already loaded.
I use Komodo Studio, and tagging variables with #var, even inside methods, preserves auto completion for me.
namespace MyProject\MyBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
class WelcomeController extends ContainerAware
{
public function indexAction()
{
/*#var Request*/$request = $this->container->get('request');
$request->[autocomplete hint list appears here]
}
}
working with netbeans IDE 7.1.2 PHP

Categories