How to acces is_* property in laravel model? - laravel-4

I am working with laravel 4.2 and have table in db with property is_active.
When I try to access this model property:
$model->is_active
I am getting following error:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
So question is how to access this property?
Please do not recommend to rename this field in the database if possible because this is already existing database in production.
Here is my model class:
class Position extends \Eloquent {
protected $table = "hr_positions";
protected $fillable = ['slug', 'info_small', 'info_full', 'is_active', 'start_date', 'end_date', 'tags', 'user_create_id', 'user_update_id'];
use \MyApp\Core\StartEndDateTrait;
public function postulations(){
return $this->hasMany('Postulation', 'position_id', 'id');
}
}
Latest notice:
All this error ocurrs on a page where I am creating my entity. In the controller before forwarding to the page I am doing:
$position = new \Position();
and then, for example, following code produce error as well:
dd(($position->getAttribute('is_active')));
but if I replace $position = new \Position(); with
$position = \Position::first();
error is gone?
What is going on here?????

Laravel does a lot of magic behind the scenes, as in, calls a lot of php magic methods.
If a called property is not defined, __call is invoked which in Eloquent calls getAttribute().
Steps taken by getAttribute($key) are
Is there a database field by this key? If so, return it.
Is there a loaded relationship by this key? If so, return it.
Is there a camelCase method of this key? If so, return it. (is_active looks for isActive method)
Returns null.
The only time that exception is thrown is in step 3.
When you create a new instance, eloquent has no idea what kind of fields it has, so if you have a method by the same name, it will always throw a relation error, this seems to be the case in both Laravel4 and Laravel5.
How to avoid it? Use the getAttributeValue($key) method. It has no relation checks and returns null by default.
Alternatively you can also add a get mutator for your field.

I have found a hack for this. Still not ideal but at least I have some solution. Better any than none.
So This code produce problem:
$position = new \Position();
if($position->is_active){
//
}
and this one works fine, this is solution even hacky but solution:
$position = new \Position(['is_active' => 0]);
if($position->is_active){
//
}
I will wait if someone give better, cleaner solution. If no one comes in next few days I will accept mine.

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.

The Laravel $model->save() response?

If you are thinking this question is a beginner's question, maybe you are right. But really I was confused.
In my code, I want to know if saving a model is successful or not.
$model = Model::find(1);
$model->attr = $someVale;
$saveStatus = $model->save()
So, I think $saveStatus must show me if the saving is successful or not, But, now, the model is saved in the database while the $saveStatus value is NULL.
I am using Laravel 7;
save() will return a boolean, saved or not saved. So you can either do:
$model = new Model();
$model->attr = $value;
$saved = $model->save();
if(!$saved){
//Do something
}
Or directly save in the if:
if(!$model->save()){
//Do something
}
Please read those documentation from Laravel api section.
https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_getChanges
From here you can get many option to know current object was modified or not.
Also you can check this,
Laravel Eloquent update just if changes have been made
For Create object,
those option can helpful,
You can check the public attribute $exists on your model
if ($model->exists) {
// Model exists in the database
}
You can check for the models id (since that's only available after the record is saved and the newly created id is returned)
if(!$model->id){
App::abort(500, 'Some Error');
}

Laravel with() together Table or Variable Name

I was reading a blog related Repository Pattern. I saw use of withBlogs method.
public function index()
{
$blogs = $this->blogRepository->all();
return view('blog')->withBlogs($blogs);
}
I never see something like this before. What is the purpose of it or what it's doing?
it is laravel's magic methods
you can name the method anything you want with with() in laravel
let me explain you by example, the following code you write in your controller method
return view('index')->withName('Name')->withFullName('Full Name')->withaddress('Your address')->withcountryName('CountryName');
then you can access the values in view explained below
withName('Name') in view it becomes $name
withFullName('Full Name') in view it becomes $fullName
withaddress('Your address') in view it becomes $address
withcountryName('CountryName') in view it becomes $countryName
It is used for passing data into views. The with method returns an instance of the view object so that you can continue chaining methods before returning the view. All of the syntax below archives the same thing:
return view('blog')->withBlogs($blogs);
return view('blog')->with('blogs', $blogs);
return view('blog')->with(compact('blogs'));
return view('blog', compact('blogs'));

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.

Eloquent relations - attach (but don't save) to Has Many

I have the following relations set up:
class Page {
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment {
public function page() {
return $this->belongsTo('Page');
}
}
Pretty bog standard. One page can have many comments, and one comment belongs to a single page.
I'd like to be able to create a new page:
$page = new Page;
and a comment
$comment = new Comment;
and attach the comment to the page, without saving any of it
$page->comments->associate($comment);
I've tried the following:
// These are for one-to-many from the MANY side (eg. $comment->page->associate...)
$page->comments->associate($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::associate()
$page->comments()->associate($comment); // Call to undefined method Illuminate\Database\Query\Builder::associate()
// These 2 are for many-to-many relations, so don't work
$page->comments->attach($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::attach()
$page->comments()->attach($comment); // Call to undefined method Illuminate\Database\Query\Builder::attach()
// These 2 will (if successful) save to the DB, which I don't want
$page->comments->save($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::save()
$page->comments()->save($comment); // Integrity constraint violation: 1048 Column 'page_id' cannot be null
The really odd thing is that doing the opposite (attaching the page to the comment) works correctly:
$comment->page()->associate($page);
The relevant docs are here but they don't make any mention of attaching to the ONE side of a one-to-many. Is it even possible? (I feel like it should be)
It sounds like you just want to add the new comment object to the page's comments collection - you can do that easily, using the basic colection add method:
$page = new Page;
$comment = new Comment;
$page->comments->add($comment);
You can't do since there are no ids to link.
So first you need to save the parent ($page) then save the child model:
// $page is existing model, $comment don't need to be
$page->comments()->save($comment); // saves the comment
or the other way around, this time without saving:
// again $page exists, $comment don't need to
$comment->page()->associate($page); // doesn't save the comment yet
$comment->save();
according to Benubird I just wanted to add something as I stumbled over this today:
You can call the add method on a collection like Benubird stated.
To consider the concerns of edpaaz (additional fired query) I did this:
$collection = $page->comments()->getEager(); // Will return the eager collection
$collection->add($comment) // Add comment to collection
As far as I can see this will prevent the additional query as we only use the relation-object.
In my case, one of the entities were persistent while the first (in your case page) was not (and to be created). As I had to process a few things and wanted to handle this in a object manner, I wanted to add a persistent entity object to a non persistent. Should work with both non persistent, too though.
Thank you Benubird for pointing me to the right direction. Hope my addition helps someone as it did for me.
Please have in mind that this is my first stackoverflow post, so please leave your feedback with a bit concern.

Resources