Laravel Relationship Without Timestamps - laravel

Is there away to call a model with a relationship without timestamps when calling the with statement in the model itself - instead of going into that relationship model and saying $protected timestamps = false;
e.g
class Post Extends Eloquent {
public function template() {
return $this->hasOne('App\Template', 'id', 'template_id')->timestamps(false);
}
}

You can add this in following model to skip the values
protected $hidden = [
'created_at',
'updated_at'
];

Related

Laravel 9 - specular model relations not working

i can't figure out why my eloquent relationship is not working.
I have the grid and details tables related (es. grid.id = details.order_id). This works for first 2 tables but not for other 2 that are almost a copy of the first.
Working relationship code :
OrderGrid model:
use HasFactory;
protected $table = 'orders_grid';
protected $fillable = [
'user_id',
// more data
];
public function order_detail()
{
return $this->hasMany('App\Models\OrderDetail');
}
OrderDetail model:
use HasFactory;
protected $table = 'orders_detail';
protected $fillable = [
'order_id',
// more data
];
public function order_grid()
{
return $this->belongsTo('App\Models\OrderGrid', 'order_id', 'id');
}
In controller
$data_details = OrderDetail::where('order_id', $id)->get();
in view
$data_details->first()->order_grid->id
// so i can get the ID from the related table
Now i have two more very similar models which relations are not working:
OrderGrid model 2
use HasFactory;
protected $table = 'orders_grid_jde';
protected $fillable = [
'total_order',
// more data
];
public function detail_jde()
{
return $this->hasMany('App\Models\OrderDetailJde');
}
OrderDetail model 2 :
use HasFactory;
protected $table = 'orders_detail_jde';
protected $fillable = [
'order_jde_id',
];
public function grid_jde()
{
return $this->belongsTo('App\Models\OrderGridJde', 'order_jde_id', 'id');
}
In controller:
$data_details = OrderDetailJde::where('order_jde_id', $id)->get();
In view:
$data_details->first()->order_jde_id->id
This is not working, if i dump($data_details->first()) relations is empty array, not as in the screen shown before
Another weird behaviour on working relation: if i dump $data_details->first() in the controller i don't see the relation, but if i do it in view it shows as in the screen.
Sorry for the long post, tried to be understandble as possible
In your Controller call the relation like this:
$data_details = OrderDetailJde::where('order_jde_id', $id)->with("grid_jde")->get();
In view:
$data_details->first()
It will give all the detils.
It should work now.
In the view where you have the error, you are accessing the directly to the field named order_jde_id instead of the relationship grid_jde.
Tips:
You should eager load the relationships if you are planning to use these in the view.
$books = Book::with('author')->get();
https://laravel.com/docs/9.x/eloquent-relationships#eager-loading

Laravel : Can appends attributes lead to a n+1 problem?

Introduction
I start using appends attributes within models during fall 2019. At start, it was simply to run function that will join two column, but now, I use them to make some analysis on certain models before sending it to the frontend. Now, I'm suspecting this to lead to a n+1 problem. So, let check on some use of the appends attributes.
Basic usage
<?php
namespace App;
class User extends Authenticatable
{
protected $dates = ['deleted_at'];
protected $table = 'users';
protected $appends =
[
'full_name',
];
protected $fillable =
[
'username',
'first_name',
'last_name',
];
getFullNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
}
So, that let you append the full name, we know that, but under the hood?
$user = User::find($id);
Using appends with the above model retrieve is almost incensitive for any application. If we push it a little bit, what really happen?
$users = User::get();
Now, is the query to the database is the same as if we don't append anything? In other word, is the appending happen during the query to DB or after? Whatever, you need the full name and if it's not in the Database or the server, you will do it in the frontend, so that need to happen anyway. Which of the three is the fastest is for another question.
A bit more complexe usage
namespace App;
class User extends Authenticatable
{
protected $dates = ['deleted_at'];
protected $table = 'users';
protected $appends =
[
'full_name',
'last_post',
];
protected $fillable =
[
'username',
'first_name',
'last_name',
];
protected $hidden =
[
'password',
'remember_token',
];
public function posts()
{
return $this->hasMany('App\Post', 'user_id');
}
getFullNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
getLastPostAttribute()
{
return $this->posts()->last()
}
}
Now it's obvious that if I do $users->get(); it will result into a n+1 problem. But if I do this $users->with('posts')->get(); Do I have the n+1 problem or worst?
If your not use with n+1 problem
The n+1 problem is when you create a query that will need to make the initial call to DB and one call for each object into the array. $users->get() will get all users and then, since I append the last_post, since post where not retrieved, it will make a DB call for each of the models.
What I want to know, it's in the second case $users->with('posts')->get(), when Laravel will append last_post, will it know that it already have all the posts? So it doesnt need to load them a second time for each users?
So this attribute will cause n+1 queries no matter what you preload
getLastPostAttribute()
{
return $this->posts()->last()
}
By accessing posts() as a method you are creating a new query builder instance.
Replace it with return $this->posts->last() and then you can preload you posts relationship and avoid your extra queries
If you are unsure if you are getting n+1 issues you can require this great package from beyondcode beyondcode/laravel-query-detector which will inform you of any n+1's you have.
https://github.com/beyondcode/laravel-query-detector
To fix your n+1...
$users = Users::with(['posts'])->get();
public function getFullNameAttribute()
{
return $this->first_name.' '.$this->last_name;
}
public function getLastPostAttribute()
{
return $this->posts->last(); //update to not use builder instance
}

In Laravel - Add a variable to a model, without putting it in the database

I have a Team-model that has been used several places, and which returns the fields from the database in an API-endpoint.
It's currently accessed and returned like this:
$team = Team::find(1)
return $team;
I would like to add a calculated variable to that returned Collection. I imagined that I could add it to the constructor of the Model, and thereby get it with all the places where the Team-model is currently used, like this:
class Team extends Model
{
protected $table = 'teams';
protected $fillable = [
'id',
'created_at',
'updated_at',
'team_name'
];
public $number_of_players;
public function __construct( array $attributes = [] ){
$this->number_of_players = 3; //This number should be calculated
parent::__construct( $attributes );
}
}
But that doesn't work.
How do I add a variable to all the places, where the Team-model is fetched?
I also looked into API Resources. I looks like that that could be a solution, but I found it pretty verbose and a long-haired solution (plus, I couldn't get it to work either).
You can use accessor/mutator
Suppose you have a relationship
Team->Player (Team hasMany Players)
You can do like
in Team model
class Model extends Model {
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
}
now you can make it
<?php
class Model extends Model {
protected $appends = ['number_of_players'];
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
public function getNumberOfPlayersAttribute()
{
return $this->players->count();
}
}
And then access the players count of a team like App/Team::find(1)->number_of_players

Can eloquent ignore irrelevant data in Laravel 5.7

This is basically the same question as this here from 2013. Except the answer doesn't work for me.
I have a Model App\Post:
class Post extends Model
{
protected $fillable = ['title'];
// This Model doesn't contain an 'authorname' field
public function author()
{
return $this->belongsTo('App\Author');
}
}
and a Model App\Author:
class Author extends Model
{
protected $fillable = ['name'];
public function posts()
{
return $this->hasMany('App\Post');
}
}
And an array I want to save to that Model:
$posts = [
['title'=>'one post', 'authorname' => 'Mickey'],
['title'=>'another post', 'authorname' => 'Minny'],
];
foreach($posts as $post){
$authorModel=App\Author::firstOrCreate(['name'=>$post['authorname']]);
App\Post::create($post)->author()->associate($authorModel)->save();
}
According to this question, that should work, but I get an
SQL error 42522: Column not found: 1054 Unknown column 'authorname' in 'field list'
which suggests Laravel forwards the whole array to mySQL. Is there a way to make this work without unsetting the authorname key?
Obviously this is a simpified version of what I want to do and keeping track of what to unset seems unnecessary - as would be assigning all array keys to their respective database fields manually.
The only idea I have here is that you run this code in DatabaseSeeder (which automatically unguards models) or you somewhere manually call Eloquent::unguard() (or code similar to this). This would explain why any other fields are used when creating model no matter of $fillable property.

laravel select statement calling sub model using "with" statement

When I call a select statement using a with clause calling supplier model I want the supplier class to call the user related model. how do I do that
$products = Product::select(['id', 'title', 'slug', 'unit_price',
'sell_price', 'created_at', 'updated_at', 'created_by',
'updated_by', 'supplier_id'])->with('supplier');
// when calling supplier model call user model as well
class Supplier extends Model
{
protected $table = "suppliers";
protected $fillable = ['company', 'email'];
public function user() {
return $this->belongsTo('App\User', 'email', 'email');
}
}
According to the documentation you can perform nested eager loading using the dot notation.
So in you example
...->with('supplier');
becomes
...->with('supplier.user');

Resources