Serialize Eloquent collection append sometimes - laravel

I want to add value from accessor to collection in specific cases. There is array $appends for it, but I don't want to add the values always. Maybe is there a method like makeVisible for it?
Lets imagine I have models Book and Author:
class Book extends Model
{
protected $fillable = ['title', 'author_id'];
public function author()
{
return $this->belongsTo(App\Author::class);
}
public function getAuthorNameAttribute()
{
return $this->author->name;
}
}
And I want to return books collection with author name just call:
Route::get('books', function () {
return App\Book::all()
->appends(['author_name']);
});
And yes I'm able to use map() but I just find another (more beautiful decision)

Yes, Laravel eloquent has a method called makeVisible().
If you would like to make some typically hidden attributes visible on
a given model instance, you may use the makeVisible method. The
makeVisible method returns the model instance for convenient method
chaining.

Related

How to define multiple belongsTo in laravel

My table has many foreign key for example prefecture_id, gender_id and status_id.
And I made model for those table.
So I want to define multiple belongsTo method like following for get all data with query builder..
But In fact belongsTo can't use like this.
public function foreign(){
return $this->belongsTo([
'App/Prefecture',
'App/Gender',
'App/Status',
]
}
And if the only way is defining multiple method for belongs to.
How do I get all belongstos data in querybuilder.
Please give me advice.
As far as I am aware, there's not a way to get multiple belongsTo from a single method. What you have to do is make one method for each relationship and when you want to load the relationships you can do the following.
Model
public function prefecture()
{
return $this->belongsTo(\App\Prefecture::class);
}
public function gender()
{
return $this->belongsTo(\App\Gender::class);
}
public function status()
{
return $this->belongsTo(\App\Status::class);
}
Query
// This will get your model with all of the belongs to relationships.
$results = Model::query()->with(['prefecture', 'gender', 'status'])->get();

laravel hide result for all relations if column has specific value

Lets assume I have a table posts with the fields id and content and published.
A User can have multiple posts, and a post can belong to multiple pages and there might be a lot more relations to a post.
Lets say we have an admin that wants to moderate the posts, the posts should only be visible if approved. So I add the boolean published where posts that are not published 0 should never be visible (only in specific cases e.g. to moderate the post).
Is it possible to set something in the Post model to restrict the related models from loading non published posts.
I want to avoid that I have to filter in the relation, e.g. if I call $user->posts I do not want to check if the posts are published, the non published results should not be available only if i do a search like. Post::where('published','0'). Basically something like softdeletes but than with a custom field.
An example, where the opposite relations are also defined, to make it easier to understand would be:
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
public function collection()
{
return $this->belongsTo('App\Collection');
}
public function style()
{
return $this->belongsTo('App\Style');
}
public function pictures()
{
return $this->hasMany('App\Picture')->orderBy('priority', 'asc');
}
public function user()
{
return $this->belongsTo('App\User');
}
}
You can use global query scope in the Model as below to add your desired filters to each query, like below:
// Post.php
class Post extend Model
{
protected static function booted()
{
static::addGlobalScope('published', function (Builder $builder) {
$builder->where('published', true);
});
}
// ...
}
Whenever you don't want to apply the global query scope, use withoutGlobalScope with the name of the query scope, like below:
Post::withoutGlobalScope('published')->get();

How to create a universal getter/mutator for datetimes in Laravel?

I have created one and I thought it works:
<?php
namespace App\Traits;
use Carbon\Carbon;
trait FormatDates
{
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (strtotime($value))
$this->attributes[$key] = Carbon::parse($value);
}
}
But there is a problem when calling related models. For example if you have an Article and Tag model and you want to get all tags like this:
$article->tags
it returns null because of that getter mutator.
How to fix this?
update 17.11.2017
I have found a solution to my problem. The best way to present the date in locale is to use this function:
\Carbon\Carbon::setToStringFormat("d.m.Y H:i");
simply create a service provider or a middleware and it will show all $dates in format you want. There is no need to make a getter.
Based from this: https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Concerns/HasAttributes.html#method_getAttribute
The description says:
Get a plain attribute (not a relationship).
Luckily there are another two methods below it called getRelationValue and getRelationshipFromMethod, and it reads:
Get a relationship.
Get a relationship value from a method.
respectively.
And in your example, it looks like you're calling a relation.
I think you should consider it when doing your universal getter/mutator.
UPDATE:
If you inspect the code, the getAttribute also calls the getRelationValue method. But it is the last resort of the function; if the key is neither an attribute or has a mutator or is a method of the class.
Here is the stub: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L302
/**
* Get an attribute from the model.
*
* #param string $key
* #return mixed
*/
public function getAttribute($key)
{
if (! $key) {
return;
}
// If the attribute exists in the attribute array or has a "get" mutator we will
// get the attribute's value. Otherwise, we will proceed as if the developers
// are asking for a relationship's value. This covers both types of values.
if (array_key_exists($key, $this->attributes) ||
$this->hasGetMutator($key)) {
return $this->getAttributeValue($key);
}
// Here we will determine if the model base class itself contains this given key
// since we don't want to treat any of those methods as relationships because
// they are all intended as helper methods and none of these are relations.
if (method_exists(self::class, $key)) {
return;
}
return $this->getRelationValue($key);
}
ANOTHER UPDATE
Since you've changed your question:
You can just put the attribute name to $casts or $dates array (in your Model) so Laravel will automatically transform it into a Carbon instance when accessing it, like this:
class Article extends Model {
...
protected $dates = ['some_date_attribute`];
or with $casts
...
protected $casts = ['some_date_attributes' => 'date'];
You really can avoid this, it's already there!
on the model Class you can do:
protected $dates = ['nameOfTheDateOrTimestampTypeField','nameOfAnotherOne'];

Simple Laravel Relationship

I have two models, one is LeadHistory and the other one is Leads.
Leads:
class Leads extends Model
{
public function lead_history()
{
return $this->hasMany('App\LeadHistory');
}
}
LeadHistory:
class LeadHistory extends Model
{
public function lead()
{
return $this->belongsTo('App\Leads', 'lead_id', 'id');
}
}
When I go into php tinker, get the first Lead ($lead = App\Leads::first();), create a new LeadHistory ($leadHistory = new App\LeadHistory;) and ($leadHistory->message = 'second one';) and ($leadHistory->status_id = 11;) then try to save the leadHistory ($leadHistory->lead()->save($lead);). I get this error message:
BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::save()'
Can someone point me in the right direction, I feel like I have been following the instructions given in Laracasts but can't seem to get the LeadHistory to save with the associated Lead ID.
You’re trying to call save() on a relation rather than a model I think.
Instead, “attach” your LeadHistory model to your Lead model:
$lead = Lead::create($leadAttributes);
$history = new LeadHistory($leadHistoryAttributes);
$lead->history()->attach($history);
You’ll need to rename your relation if you copy-and-paste the above code:
class Lead extends Model
{
public function history()
{
return $this->hasMany(LeadHistory::class);
}
}
I feel the name “lead history” is superfluous when you’re already working with a Lead model.
Try to save $leadHistory first:
$leadHistory->save();
And then:
$lead->lead_history()->save($leadHistory)
Correct me if I'm wrong, but since you already have a model instance of your target App\Leads, I think you should be able to simply access the id of that instance and inject it into a static create call:
$lead = App\Leads::first();
$leadHistory = App\LeadHistory::create([
'message' => 'second one',
'status_id' => 11,
'lead_id' => $lead->id
]);
Before being able to use the create method you'd have to make the properties you want to assign 'mass assignable', by defining a protected property called $fillable in your model:
class LeadHistory extends Model
{
protected $fillable = [
'message',
'status_id',
'lead_id'
];
public function lead()
{
return $this->belongsTo('App\Leads', 'lead_id', 'id');
}
}
This will effectively associate your new record with that lead, since the only thing the Eloquent model does in this regard is providing another way to describe the same relationships your database exercises.
Some other answers mention the attach() method of an Eloquent model. This method is used to attach two models with a many to many relationship (relationships defined with belongsToMany).

Laravel + Eloquent - Passing through custom function first

What I want to do is quite simple I think. In my controller I have the code:
$list = SiteCategory::where('type','=','A')->get();
Which returns a standard eloquent collection object. However, sometimes when I retrieve categories, I want them to be ordered in a specific way first. So can I have some function in my model like:
Class SiteCategory extends Eloquent {
public function mySpecialFunction(){
// retrieve all categories, manipulate them in some way and return.
}
}
How do I then call this function? I don't understand, and the tutorials and questions I've read do not help. For example, in this question on SO, he seems to imply he can call his function something like this:
SiteCategory->mySpecialFunction()
I don't get it?
You can use a scope method inside your Model like this
Class SiteCategory extends Eloquent {
public function scopeMySpecialFunction($query){
// retrieve all categories, manipulate them in some way and return.
}
}
Then you can call this function like normal built in Eloquent functions like
SiteCategory::mySpecialFunction();
If you want to pass any arguments into this function then you can add parameters like this
public function scopeMySpecialFunction($query, $param1, $param2){
// retrieve all categories, manipulate them in some way and return.
}
Notice the first $query parameter, it's the first parameter of a scope method and $query is an instance of Illuminate\Database\Eloquent\Builder, you can use $this inside that function and can chain methods like
SiteCategory::mySpecialFunction()->find(1);
In this case you have to return the $query from your function to chain other methods, like:
public function scopeMySpecialFunction($query, $param1, $param2){
// do something
return $query;
}

Resources