How to declare columns on models? - php

I want my Code Completion (PhpStorm) to work with Eloquent models but if I declare fields corresponding to DB columns they no longer work:
class Limit extends Model
{
public $id;
public $type;
public $limit;
}
Is there some workaround?

(This is a slightly modified version of my own reply from this thread: Eloquent ORM Code Hinting in PhpStorm)
The laravel-ide-helper package can be used to help with this issue, by generating PHPDocs for your models.
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.

Related

CakePHP: How to update the Models PHPDocs?

I have a huge project with 100+ tables in my database. Over the years, we add some new columns in some tables, but we forgot to reflect this on Models phpDocs. Those phpdocs were initially generate by cake bake model in the beginning of our project.
Example:
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* Pessoa Entity
*
* #property int $id
* #property string $numero_documento_principal
* #property string $nome
* #property string $tipo_pessoa
* #property string $cidade_natural
* #property string $nacionalidade
* #property string $estado_natural
* #property \Cake\I18n\Time $data_obito
* #property string $sexo
* #property string $nome_genitor
* #property string $nome_genitora
* #property \Cake\I18n\Time $data_nascimento
*
* #property \App\Model\Entity\EnderecoPessoa[] $endereco_pessoa
* #property \App\Model\Entity\Parte[] $parte
*/
class Pessoa extends Entity {}
Those docs are very important nowadays thanks auto-complete features used in VSCode and similars IDEs.
The problem is: if we try to generate by bake again, all custom code will be replaced by the original bake generated one.
Is there a away to avoid this and update only the phpDocs part?
Put your code in a version control system and revert the changes that you do not need after baking, or use dereuromark/cakephp-ide-helper, which can update annotations.
Alternatively check if the upcoming (partially) non-destructive baking functionality works for your needs.

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.

Is there any way of using an object type in #property-read and "declare" its attributes?

I want to add a type of object in #property-read and also be able to access its attributes with PhpStorm suggestions.
Here's what I would like to do:
/**
* #property-read object{type: string, schema: string} $request_schema
* #property-read object{type: string, schema: string} $response_schema
*/
class ConfigApiRoutesSchema extends BaseSchema
{
...
}
The reason I'm doing this is because I want to access the attributes type and schema, like this:
$configApiRoutesSchema = new ConfigApiRoutesSchema();
$configApiRoutesSchema->request_schema->type // Here's the problem, type is not suggested
$configApiRoutesSchema->request_schema->schema // schema is also not suggested
This is exactly what I need, but unfortunately this doesn't work for some reason. One solution is to create two classes and specify them as types:
/**
* #property-read string $type
* #property-read string $schema
*/
class RequestSchema {}
/**
* #property-read string $type
* #property-read string $schema
*/
class ResponseSchema {}
/**
* #property-read RequestSchema $request_schema
* #property-read ResponseSchema $response_schema
* #
*/
class ConfigApiRoutesSchema extends BaseSchema {}
Now PhpStorm suggests both schema and type.
But it doesn't seem correct, since RequestSchema and ResponseSchema won't actually be used. So I wonder if there's any way of using annotation with objects without having to create a class to suggest its attributes.
Thank you.
Altho it is possible to specify the shape of arrays, for objects it is not possible to do so (yet). Such a syntax would have been be specified in PSR-5 and PSR-19, but this is not the case.
In the same manner it is not possible to specify the properties of a returned plain object from a method with the #return object annotion.
Apparently the syntax is supported by Psalm, but not in PhpStorm (at very least not yet).
Currently there is a public proposal on the PhpStorm YouTrack board. You might want to upvote that issue to increase it's chance of being included in the next PhpStorm version.

Laravel vs Symfony | Model Attributes

I'm actually discovering Laravel (I've used symfony for years) and I face a situation where I don't know if I just couldn't find the right information or it is just the way to do in Laravel.
When creating a model in Symfony you put attributes in your model class. Actually these attributes represent the different columns of your table for the ORM.
Now in Laravel I see all people don't put theses attributes in the model class but in the migrations files. So a new developer who should contribute in a new project will have to look on database or migrations files. Which in my opinion is not the rule of an ORM : "let dev think class and not table"
Could somebody, please, put a light on this point?
Thanks
If you use PHPStorm (but it can work with other IDEs as well) there is an interesting composer package that can generate model documentation for you, in order to have autocompletion for attributes ad methods based on your migrations:
https://github.com/barryvdh/laravel-ide-helper
its three main methods:
php artisan ide-helper:generate creates phpDoc for Facades
php artisan ide-helper:model creates phpDoc for your model (you should relaunch it after every new migration)
php artisan ide-helper:meta creates phpstorm meta file
so, if you launch php artisan ide-helper:model it will add something similar to this
/**
* App\User
*
* #property-read \Illuminate\Database\Eloquent\Collection|\Spatie\Permission\Models\Permission[] $permissions
* #property-read \Illuminate\Database\Eloquent\Collection|\Spatie\Permission\Models\Role[] $roles
* #method static \Illuminate\Database\Eloquent\Builder|\App\User permission($permissions)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User role($roles, $guard = null)
* #property int $id
* #property string $name
* #property string $email
* #property \Illuminate\Support\Carbon|null $email_verified_at
* #property string $password
* #property string|null $remember_token
* #property \Illuminate\Support\Carbon|null $created_at
* #property \Illuminate\Support\Carbon|null $updated_at
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereCreatedAt($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereEmail($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereEmailVerifiedAt($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereId($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereName($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User wherePassword($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value)
* [...]
*/
So you have a phpDoc
and your IDE should be able to autocomplete your code like this:

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

Categories