Yii2 virtual attribute getter had to change to function call - methods

I'm new to OOP and Yii2. I have a function in Model:
public function getDatRev() {
if ($this->rev) {
return $this->rev;
} else {
return $this->datum;
}
}
in the View until now I have used it like this:
$model->datRev;
and it would return the correct value. Now I don't know what has changed, maybe I was also updated the framework, but the old construct doesn't work anymore, and in order to make it work I have to change it to:
$model->getDatRev();
Can you please explain to me why that is?

When you try get property the Yii2 calls magic method __get (). Return value is depend from implementation of this method in parent class. Yii2 can check if this property exist in some container, or if exist getter of this property.
In your case seems like you don't call parent's method __get(). This may have happened because you override __get() method or initialized this property.

Your class needs to extend yii\base\Object (directly or not) in order to use short property syntax ($model->abc instead of $model->getAbc()). Magic method __get() #Timur mentioned is defined there and further extended in yii\base\Component class.

Related

Creating a Laravel attribute (accessor) on model but unable to access model properties

Here's my code:
protected function expires(): Attribute
{
if ($this->started_at) {
$expiry = $this->started_at->addDays(20);
}
return Attribute::make(
get: fn () => $expiry ?? null
);
}
Running this code gives me an ErrorException with the message Undefined property: Models\Job::$started_at
I have found that I can work around this error by accessing the property through $this->attributes['started_at'] as follows:
protected function expires(): Attribute
{
if ($this->attributes['started_at']) {
$expiry = Carbon::parse($this->attributes['started_at'])->addDays(20);
}
return Attribute::make(
get: fn () => $expiry ?? null
);
}
However, this code feels a little inefficient because I'm manually using Carbon to parse the property back into a Carbon object. But if I do a dd($this->started_at) right before the if statement, it's already been cast to a Carbon object by Laravel and I'd really just like to use this object to make my code as clean as in the first example above.
I'd like to know the reason why $this->started_at is apparently available as a Carbon object in this context but somehow not usable (an undefined property) in the way I'm using it, and also I would like to know if there is another way to go about achieving my goal?
you can add custom attributes with
public function getExpireAttribute()
{
if ($this->started_at) {
$this->started_at->addDays(20);
}
return $this->started_at;
}
now you can access expire attribute like other, with
$model->expire
to make Eloquent casts dates to Carbon for you, add attribute to casts:
protected $casts = [
'started_at' => 'datetime',
];
The reason you are getting an "Undefined property" error when trying to access $this->started_at in your accessor method is because Laravel's model accessor methods are executed before the model attributes are hydrated.
This means that when your expires() method is executed, the started_at attribute may not have been set yet, and thus accessing it directly on the model instance will result in an "Undefined property" error.
One way to work around this is to use the getAttribute method provided by Laravel's Model class. This method allows you to retrieve the value of an attribute, even if it has not been set yet. Here's an updated version of your expires() method that uses getAttribute:
use Carbon\Carbon;
protected function getExpiresAttribute(): ?Carbon
{
$startedAt = $this->getAttribute('started_at');
if ($startedAt) {
return $startedAt->addDays(20);
}
return null;
}
In this version, we are using the getAttribute method to retrieve the value of the started_at attribute, even if it has not been set yet. We then use Carbon to manipulate the date, and return the result.
Note that we are using the getExpiresAttribute method instead of the expires method, because Laravel automatically maps get{AttributeName}Attribute method calls to corresponding attribute accessors. So, in this case, calling
$model->expires
will automatically execute the getExpiresAttribute method.
With this approach, you can use the started_at property directly in your code, and it will be automatically cast to a Carbon object by Laravel, without the need to manually parse it with Carbon.
Hope this helps.

Cannot set property for Model class in Laravel

I'm trying add dependency injection on User class and after few hours trying, I found this way:
I added this code to App\Models\User class
public function __construct()
{
$this->roleService = resolve(RoleService::class);
}
I've checked $this->roleService value, it work normally, buut when I try to use it in a function:
public function isAdmin() {
return $this->roleService->isAdmin($this->role_id);
}
This function throw an error Call to a member function isAdmin() on null. When I logged $this->roleService, it returned null.
Anyone can solve this?
Create a custom accessor for your model, and get/set the value from there.
public function getRoleServiceAttribute ()
{
if(is_null($this->roleService)){
$this->roleService = resolve(RoleService::class);
}
return $this->roleService;
}
Then you can access the attribute as $this->role_service
Also make sure you declare the $roleService class property as protected, to avoid accidental access as $this->roleService from anywhere else.
Thank for help. I just have referenced answer of an expert. He said that we should not dependency injection in Model class because it will broke project structure. Project should be followed structure: Controller->Service->Repository->Model->Database. So, I'll refactor my code follow it.

laravel: function in model must return a relationship instance

I try to build a path for a model on laravel
I created a function in my model:
public function path()
{
return App\Helper\GeneralController::getURL($this);
}
with dd(App\Helper\GeneralController::getURL($this)) test I got the right answer. (output is a URL)
but in view with the call: $article->path I get this error:
App\Article:: path must return a relationship instance.
What is wrong?
You need to call it:
$article->path()
When you do $article->path, you're trying to use Eloquent relationship which you don't have.
I know this has already been answered and accepted. However, if the OP did want to use a property accessor rather than a method use the "get{property name}Attribute" syntax of Laravel to create a custom attribute.
Here is what it would look like for this specific case:
public function getPathAttribute()
{
return App\Helper\GeneralController::getURL($this);
}
using this approach "path" can now be called as an attribute and will not be resolved to a relationship using the syntax:
$article->path;
You're calling a relationship.
$article->path
To call the method, use '()', like so,
$article->path()
I faced that error when I forgot to write return before relation in the model!
check it out now!
path() is method not object element you need to call as method
$article->path();
Laravel 9 introduced a new way to define accessors/mutators within a model using Illuminate\Database\Eloquent\Casts\Attribute.
https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor
public function path(): Attribute
{
return new Attribute(fn () => GeneralController::getURL($this));
}
For future visitors from Google, all the other answers can be applicable in certain scenarios, but you might want to also look if your method access modifier, if your method is protected and you try to call it you will be welcome with this error. You need change your method to public.

With Codeigniter, what's the difference if I wanted to set a method to private?

With callback functions that are within the same class, I can't set the following.
private function check_valid_image
{
...
}
I can get it to work if I do the following.
function _check_valid_image
{
...
}
By placing an underscore in front of the method name, is that the same as placing the word private in front?
It's a convention used with the form validation class for callbacks. It also makes that method not callable via the URL segments.
That being said, it is not the equivalent of making a method private, which has implications in how code can be run outside of the class.

Wrapper class does not save to database when set

I have a wrapper class that is meant to get and set code from a property in one of my dbml's partial classes. The reason for the wrapper is for a specialized get, which pre-formats the value. Here's what the wrapper looks like (NOTE: this is not the actual code, but represents everything but the formatting accurately):
partial class Class1
{
public string PropertyFormatted
{
get
{
var ret = Property.Substring(1);
return ret;
}
set { Property = value; }
}
}
This wrapper is bound using Bind() in a formview for the edit page. For some reason, the wrapper's value is set twice on update and the second time through the value is re-assigned its original value (causing the property to remain, ultimately, unchanged). However, when the wrapper is replaced with the property itself, there is no problem with saving to the database.
Any ideas what may be the cause of this?
The dbContext should be automatically detecting changes via this method:
http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbchangetracker.detectchanges(v=vs.103).aspx
You may have inadvertently disable auto detect changes or something of the like. Try manually calling the method and see if that makes a difference.
Good luck!

Resources