PhpStorm CodeIgniter class not found - php

Can you help me on this one. I'm new in CodeIgniter and PhpStorm, I'm having a problem. The PhpStorm IDE is showing an error, even though the code works fine (the Persons(controller) class can't find the Person(model) class).
$data['persons']=$this->**person**->get_person(); = on this syntax from Persons class, there is a message "Field person not found in class Persons".
Can you enlighten me on how to resolve this, but actually the output is good and it retrieves the data without issue just a warning message in Persons class.

The person property ($this->property) is not explicitly declared as it is created and accessed via PHP's magic methods.
PhpStorm has no special support for CodeIgniter framework and cannot guess where $this->person is coming from and what type it is.
But you can help IDE -- just a small PHPDoc comment before the actual class -- using #property tag.
/**
* #property Person $person Optional description
*/
class Persons extends CI_Controller {
...and oh magic -- it works:

The answer from LazyOne did not work for me initially. After some testing I found out that my issue was upper/lower case related on how you declare the property in PHPDoc - hopefully the following observations can help someone else. This is what I have to declare my model class:
class Custom extends CI_Model {}
In my controller I load and use the model for example the following way:
$this->load->model('Custom')
$table = $this->Custom->get();
Now for phpStorm to pick up this class correctly I originally added a PHPDoc #property comment above a core class as described by others (either above the CI_Controller class or separate CI_phpStrom.php file) like this:
*
* #property Custom $custom
*
However, this didn't remove the issue, as the variable name becomes case sensitive in this case and I had to write:
*
* #property Custom $Custom
*
for my above controller code to pick up the class correctly. An alternative would be to use lowercase when calling functions (this works even if your model declaration used uppercase)
$this->load->model('custom')
$table = $this->custom->get();
The funny thing all this uppercase or lowercase didn't matter if I call my model class "Custom_model" - then it made no change if set the PHPDoc property variable to $Custom_model or $custom_model ...

Normally most people add the ‘_model’ suffix to the Model class names. I suggest that you rename your model with "Person_model" call your model "person_model" like this:
$this->load->model('person_model');
$this->person_model->get_person();
I think that you might find it an adequate solution!

Related

How to prevent a false-warning in PhpStorm when the return-type is deduced incorrectly?

Consider the following code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
/**
* Class MyModel
* #package App\Models
* #mixin Builder
*/
class MyModel extends Model
{
public static function getGreens(): Builder
{
return (new self())->where('color', '=', 'green');
}
}
On the return statement, the PhpStorm (2020.3) complains that:
Return value is expected to be '\Illuminate\Database\Eloquent\Builder', 'MyModel' returned
And suggest to:
Change return type from '\Illuminate\Database\Eloquent\Builder' to 'MyModel'
which is weirdly incorrect (the where method does return an instance of \Illuminate\Database\Eloquent\Builder, while the IDE deduces the return type as being of MyModel type). By removing the return type, the IDE issues another warning:
Missing function's return type declaration
The code works without any problems, but the IDE shouldn't report any false warnings! How should I avoid these warnings in PhpStorm?
It's a result of not following the "best practices". The class-hierarchy of MyModel does not provide a method of where; in other words, such a method does not exist in the class-hierarchy. But! The parent class of Model does provide a magic method of __call() which gets triggered when an inaccessible method in the object context is invoked (in your case, the method of where). It essentially forwards the "call" to a new instance of \Illuminate\Database\Eloquent\Builder, that has the implementation of the requested method (it's acquired from invoking the method of newQuery()). This mechanism is not only IDE-unfriendly, but also slower.
Thus; drop the #mixin tag, and instead of utilizing the "magic methods", use "native access":
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class MyModel extends Model
{
public static function getGreens(): Builder
{
return (new self())->newQuery()->where('color', '=', 'green');
// ^^^^^^^^^^^^
}
}
As I understand (based on how Laravel works) it's because of #mixin line.
#mixin tag works similar to how PHP's native trait works. So if you have a method in a trait that returns $this / self and then use that trait in a class, then return of that method ($this/self) points to the class where it is used.
Now, Builder::where() method also returns $this or self ... but it's not actually a trait but Laravel magically makes that where() method available in this class.
And here comes the problem: that #return $this actually points to the Builder class but when used "as a trait" (because of #mixin) it gets resolved to the current MyModel class by the IDE.
You either use #mixin and live with ignoring the issue (you can use error suppression via Alt + Enter quick fix menu -- it will add a comment for IDE to tell to ignore that specific issue here) .. or remove #mixin and declare those methods differently.
AFAIK Laravel helper package should add all such Builder methods to the Model class via #method PHPDoc lines (look into that for details, go through past issues there to see how and why it does that etc, e.g. #541).
Another suggestion: try Laravel Idea plugin -- it's a PAID plugin but it makes working with Laravel code much easier and AFAIK it should cover such basic stuff.
For reference:
https://youtrack.jetbrains.com/issue/WI-1730 -- original request for documenting/supporting mixins
Outstanding #mixin tickets that can be related here (at least partially):
https://youtrack.jetbrains.com/issue/WI-34809
https://youtrack.jetbrains.com/issue/WI-49567
or try your own search, e.g. https://youtrack.jetbrains.com/issues/WI?q=mixin%20laravel

Laravel models: Where are model properties?

(I come from Visual Studio + Entity Framework background and trying to locate equivalent functionality in Laravel + Eloquent)
In EF and Visual Studio, we add a new Model to our application and just tell it about our existing database. EF can then generate Models for my tables with public properties for columns. This gives us all those IDE and compiler benefits such as Intellisense, spelling-error detection etc.
I've recently stated exploring VS Code, Laravel and Eloquent. Going through all those tutorials and articles, I'm not sure when and how these properties are generated in the model classes. I just tried artisan make:model command and it did generate the model class, but there are no properties in it. So,
Am I supposed to write them by hand? (really?)
Will these just be public variables or standard properties with getter/setter (excuse me for my .NET mentality :))?
Is there a tool/extension that could examine my database and create models with properties for their columns?
Update
To the people who answered my question, thanks a lot. Plus some of the comments I posted were due to my ignorance about PHP's (strange IMO) approach about member access; I just found out that PHP does not complain about non-existing class members and instead generates them on the fly (e.g. $post->NonExistingMember = SomeValue runs okay; this would not even compile in most other languages that I know). Big surprise for me. I have used C++, VB, C#, Java among several other languages and haven't seen that behavior anywhere else. All those languages would throw a compile-time error straight away saying something like Type X does not contain a member named Y. Cannot see how PHP's different approach fits together with OOP.
The actual problem that I posted this question for still remains unresolved. Although I can use reliese/laravel to generate Model classes for my database, the tool still does not generate class members against table columns, so I do not get auto-complete benefits. I'd love to hear from the experts if that can be done (automatically of course).
Update 2
Now that I understand Laravel environment slightly better, I thought I'd share my experience. See my answer below.
Now that I have spent some time with Laravel, Eloquent and PHP in general, I'll share a few things in the hope that these helps other starters.
PHP is a dynamic language and its code is compiled on the fly (this is unlike C# and VB.NET). Your model classes do not need to explicitly define members for them to be accessible/assignable, so as long they extend Model (a built-in class in Laravel Eloquent), you can assign values to non-existing members that have the same name as the underlying database table column and Eloquent will store it in the DB for you. So for example, if you have a posts table in your database that has a column named body, you can write the following code to create a new record in your database:
$p = new Post;
$p->body = 'Some stuff';
$p->save();
Of course you need to have a class Post in your project that extends from Model but you don't need to define a member body inside that class. This would sound strange to the people coming from .NET world, but that's how dynamic languages work.
As for automatically generating models, Laravel includes built-in commands (php artisan make:model) that can generate those for you.
Lastly, for intellisense and auto-complete, use the same tool that is used by Laravel itself, i.e. DocBlocks. These are special type of comments in PHP using which you can document your code elements. So you can add DocBlocks to all your model classes containing property names and types. Fortunately for everyone, there is a very neat extension in VS Code that can do this automatically for you. Install it using the following command:
composer require --dev barryvdh/laravel-ide-helper
Now run the following command to generate DocBlocks for all of your model classes (obviously you should already have generated your database and models before this):
php artisan ide-helper:models --dir='app'
The extension will fetch the structure of your database and inject DocBlocks to all your models, which will look something like this:
/**
* App\User
*
* #property int $id
* #property string $name
* #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 whereId($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\User whereName($value)
* #method static \Illuminate\Database\Eloquent\Builder|\App\Exam whereUpdatedAt($value)
* #mixin \Eloquent
*/
class User extends Model
{
}
VS Code will now show you table field names in model properties, like this (see how intellisense brings up name member from our DocBlocks as we type na...):
Note that I also have Intelephense installed in my VS Code, though I'm not sure if that is required for auto-complete feature to work.
Edit
Dynamic Properties have been deprecated in PHP 8.2 and I'm hearing that they'll become invalid in PHP 9.0, which means Laravel models should not be able to do this magic stuff in the future versions.
I'm not a PHP guru, but I hear that we don't need to panic. Two things: Firstly, the objects implementing __get and __set will keep working fine. Secondly, Plus you (they) can also use #[AllowDynamicProperties] on model classes to allow dynamic props. And lastly, they can rewrite model generator to spit out column names as props in the model class. This last one will be the best and will take PHP one step closer to how C# world works (precisely where this post started, lol).
I use annotations to declare all the properties for autocomplete (works in PHPStorm, not sure about other IDEs).
/**
* #property $id
* #property $microsoft_id
* #property $name
* #property $qualification
* #property $company
*/
class ShopTenant extends Model
{
public $connection = 'shop';
public $table = 'tenants';
protected $guarded = ['id'];
}
You can use something like this to get a list of all columns of a table, then paste/format that list into your annotations:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'tenants';
Unfortunatilly yes, you need to write them by hand, but only if you need to update the value of these properties, check point 2($fillable array)
You need to declare the properties that can be filled:
For example a model called Post for a database table "posts" that has 2 columns "title" and "body" :
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//not mandatory to declare the table name, Laravel will find it using reflection if you follow the naming standard
protected $table = 'posts'; //not mandatory
protected $fillable = ['title','body'];
}
In the controller:
$newPost = new Post;
$newPost->title = 'A new title';
$newPost->body = 'A new body';
$newPost->save(); //only at this point the db is modified
Or you can hide them if you return the properties in an array or a JSON response(in the Collection also the hidden ones will be displayed):
protected $hidden = [
'title',
];
Also you can inject new properties in the model using Accessors
I don't think so, you can install some Laravel VS Code plugins to make your life easier(e.g: Blade snippets)
You can check this article.
Actually you don't need to specify the properties. Its all done by laravel automatically. When you create a model laravel uses the plural (and it has a really good system for that: post becomes posts, activity becomes activities, etc.) of the classname to access the table. So you can work with an empty model without setting the $table or the $fillable/$guarded property.
// use only, if your table name differs from the models classname
$table = 'users_options'; // when you want to name your model 'Vote' but the table is called 'users_options' for instance.
// use fillable and guarded only to specify mass-assignment columns
$fillable = [whatever, ...];
$guarded = [whatever, ...];
you can access the properties whenever you want:
class Post extends Model
{
}
// would do sth like this: select name from posts where id = 1;
// without specifying anything in the model
$name = Post::find(1)->name;
You need to add by yourself. for example
$fillable = ['firstname', 'email','username'];
$guarded = ['price'];

naming convention of table, model file and model class in cakephp

I am using cakephp 2.6.9. I have a table named: chat_info and model file: ChatInfo.php and class inside ChatInfo:
<?php
/**
*
*/
class ChatInfo extends AppModel
{
var $name = "chatinfo";
}
?>
but it shows an error. I searched for this error and found that this is due to naming convention violation in cakecaphp. But whats wrong am I doing here
Model::useTable
As found in the docs:
The useTable property specifies the database table name. By default, the model uses the lowercase, plural form of the model’s class name
Conventions are not intended to be unbreakable rules. They are guidelines which, if followed, make life easier. That does not mean they have to be followed. Using useTable it's possible to use any table name, in this case:
class ChatInfo extends AppModel
{
public $useTable = "chat_info";
}
Two asides, assuming you're not actually using php4:
It is a terrible idea to set the model name to something other than the actual model name. It's not necessary to set it to anything, since it's designed purpose is php4 compatibility. The name of a model is the class name, setting it to something else can easily lead to confusion or unexpected side effects.
Declaring variables with var is php 4 style, use the features of the version of php in use, i.e. declare variables using public, protected or private.
if you will rename the table from chat_info to chat_infos it will be ok
Or if you want to mantain that name for the table then add this in your model:
$useTable = 'chat_info';
and your model will be associated to the table chat_info (without plural mode)

PHP annotations used in YII

Going through the auto generated code of YII for a Model class, I understand that the table columns are injected into the Model class through Annotations (#property)
<?php
/**
* This is the model class for table "tbl_project".
*
* The followings are the available columns in table 'tbl_project':
* #property integer $id
* #property string $name
*/
class Project extends CActiveRecord
{
Here property $id and $name become part of the Project class and can be accessed like such:
$proj = new Project();
$proj->id = 1;
I tried to look up annotations in PHP but found nothing as all links point to either PHPDoc . I am more interested in the Dependency Injection part of it. Can someone explain the concept please and point to a list of available annotations.
Yii does not use annotations.
It uses table schema extracted from database.
If you remove annotations all will work.
This would be interesting for you http://www.yiiframework.com/doc/api/1.1/CDbTableSchema
And here are some instructions how to speed up your app. One of ways is to enable schema caching. http://www.yiiframework.com/doc/blog/1.1/en/final.deployment
The Block comments in are only to use with PHPDoc or for your own sense.
Although my IDE (PhpStorm) uses the phpdoc block comments and its properties for code inspection and code hinting.
As said in the comments, Yii does not parse the block comments for dependency purposes.

PHP: Interfaces and inheritance in a name spaced environment

I am trying to use a interface or child class to work in a situation where I am using class hints on a method argument and keep getting the following warning:
$print_r(class_parents($listing));
$propertyTable -> getPhotos($listing);
Array ( [Tools\Object\Property] => Tools\Object\Property )
Catchable fatal error: Argument 1 passed to Tools\Db\PhotoTable::getPhotos() must be an
instance of Tools\Db\Property, instance of Tools\Object\Listing given, called in ...
and defined in ...
This is strange as you can see when tested listing extends property (see below). Why would I get this error?
I have set up a very basic test case and have figured out that type hints should accept a child class or a class that implements the required class where that class is an interface. However, in a name spaced Zend Framework 2 environment I cannot get this to work.
My code for the various classes looks like this:
namespace Tools\Db;
class PhotoTable
{
public function getPhotos(Property $propertyObject )
{
//code goes here
}
}
namespace Tools\Object;
use Tools\Object\PhotoInterface as PhotoInterface;
class property //implements photoInterface
{
public function getUrl(){ code goes here}
public function getPhotos(){ code goes here}
}//end class
use Tools\Object\PhotoInterface as PhotoInterface;
class Listing extends Property implements PhotoInterface
{
//code goes here
}
namespace Tools\Object;
interface PhotoInterface
{
public function getUrl();
public function getPhotos();
}
I can get the code above to work if I copy this all into a single file and eliminate the namespaces. Basically:
if I require property in PhotoTable I can pass Listing as it extends property.
If I require PhotoInterface I can pass Listing as it implements this interface.
But I get this weird error when I have essentially the same classes in a different files in a name spaced Zend Framework 2 environment.
Is there an extra complication in a name spaced environment I need to take into account of or am I missing something really basic.
It appears that the issue involved the name-spaced files included in the head of the script. Specifically, it appears you need to include not only the class being am passed, but also the specified parent classes or any Interface classes if they are specified in the type hint.
To give an example if my class type hint suggests I need 'property' class, then if I am sending it the 'Listing' object, it seems I need to include property in the header like this:
namespace Tools\Object;
use Tools\Object\Property; //the parent class
use Tools\Object\Listing; //the child class
This eliminates the fatal error, but it seems strange that PHP cannot automatically determine the class automatically. I assume it is something to do with the complexities of a name spaced environment.

Categories