Laravel belongs to (more than 1 table) - mysql

has a rookie in laravel i don't no very well where and how to do this.
I have 3 tables that have almost the same fields, but they all have in comon ID
public function book(){
return $this->belongsTo('App\Models\table1', 'book_id', 'id');
}
this works but can i do something like this??
What should i use?
public function book(){
return $this->belongsTo('App\Models\table1','App\Models\table2', 'book_id', 'id');
}
Thanks for sharing your knowlege.

You may define 2 separate belongsTo functions
//...
public function book(){
return $this->belongsTo('App\Book', 'foreign_key', 'primary_key');
}
public function author(){
return $this->belongsTo('App\Author', 'foreign_key', 'primary_key');
}
//...

No, you can't create a single relationship with two tables in that way, that's not the way Laravel reads the method.
It would be pretty easy, and likely most clear for code readability to just make two separate relationships:
public function book(){
return $this->belongsTo('App\Models\table1', 'foreign_key');
}
public function otherBook(){
return $this->belongsTo('App\Models\table2', 'foreign_key');
}
But, it may be worth your time to consider the overall architecture of your models and tables first. If these tables share a common ID, that's going to get quite confusing over time, and carry a lot of overhead to make sure you don't over-write. Why not just make one table with some kind of flag to identify the different types of book?
Hope this helps.

Related

Laravel accessor relationship

I have relationship batch and project
function project(){
return $this->hasMany(Project::class,'batch_id');
}
I need to get which batch is complete based on all projects (status = COMPLETED) as a Laravel accessor in model.
this is my sample data
please help. thanks!
You need to change the code from
function project(){
return $this->hasMany(Project::class,'batch_id');
}
to
function project(){
return $this->hasMany(Project::class,'project_id');
}
to get the completed batch
$batch=Project::where('status','COMPLETED')->pluck('batch_id');
In the Relationship
You can add the where inline on the relationship
function project(){
return $this->hasMany(Project::class,"bacth_id")->where("status","COMPLETED");
}
When Invoking the Relationship
If you want to keep the relationship without the where and only use it sometimes, you can call the relationship and add the where. for example, if you have an instance of your model (im going to call it $model in this example) you can do the following, notice the parenthesis next to the relationship name:
$completed = $model->project()->where("status","COMPLETED")->get();
In a Query
If you are writing a query where you are trying to pull in only versions of that model where the project is complete you can use the whereHas function. as i dont know what your model is called in your example, im just gonna call it Model
$posts = Model::whereHas("project", function (Builder $query) {
$query->where("status","COMPLETED");
})->get();

Join subquery in Laravel model

Please suggest a better title for this question. I have problems to name my question properly.
Background
I'm creating a comic database for personal use, to track my comic reads. Every comic belongs to a series. Every comics has a release date. The release date of a series is the first release of the according comic. I have a eloquent function seriesByDate() for that:
class Series extends Model
{
public $timestamps = false;
protected $primaryKey = 'series_id';
protected $fillable = ['series_name', 'publisher_id'];
public function publisher()
{
return $this->belongsTo(Publisher::class, 'publisher_id');
}
public function comics()
{
return $this->hasMany(Comic::class, 'series_id', 'series_id');
}
// instead of saving the release date of a complete series
// we look for the first comic in this series and get the
// comic's release date.
public static function seriesByDate()
{
$firstRelease = DB::table('comics')
->select('series_id', DB::raw('MIN(comic_release_date) as first_release'))
->groupBy('series_id');
$seriesByDate = DB::table('series')
->leftJoinSub($firstRelease, 'first_release', function ($join) {
$join->on('series.series_id', '=', 'first_release.series_id');
})
->join('publishers', 'publishers.publisher_id', '=', 'series.publisher_id')
->select('series.series_id', 'series.series_name', 'first_release', 'publishers.publisher_name')
->get();
return $seriesByDate;
}
What i want
I want the release_date somehow be permanent to my Series model. Meaning: When I do a App\Series::all() i already want to have the release_date as a column in my returned data. Similar to App\Series::with('publishers')->get()
With my solution above i have to eplicitly execute App\Series::seriesByDate()
Is this even possible? Can you please give me a hint?
Edit / Update
The linked video by #Musa shows how to properly do this in a model: https://stackoverflow.com/a/61558482/5754486
You can't. There is no magic for this. You might eventually write your own custom Relation but that would be unnecessarily complex, just for the sake of having a pretty related/accessor. Both solutions are not great performance-wise.
Not sure why you choose such a structure. Without any further context/explanation, I would strongly recommend you to have a release_date column directly inside your Series model as well. That will be waaaay faster than your current structure.
If you still want to stick with that structure, I would personally retrieve the release_date "php side" instead of "database side" :
$series = App\Series::query()
->with([
'publishers',
'comics' => function ($query) {
$query->orderBy('created_at');
},
])
->get();
foreach ($series as $serie) {
$serieTitle = $serie->title;
$releaseDate = $serie->comics->first()->created_at;
echo $serieTitle.' was first released '.$releaseDate->diffForHumans().'<br/>';
}
(not tested)
the only downside is that it will return a Collection of every "comics" a "serie" has. If you do not have 10k comics per serie and you do not load 1k serie per page, that should be fine. In any case, this looks more elegant and optimized/faster than your seriesByDate method.
edit: also, you should watch "Advanced Querying With Eloquent" by Jonathan Reinink, at Laracon 2018 I believe. He discusses subqueries like the one you need. I am 100% sure you will find the best and most optimal Eloquent subquery one can forge for what you are trying to achieve : https://vimeo.com/showcase/7060635/video/255049572
you can defined an accessor then append the value
class Series extends Model
{
protected $appends = ['series_date'];
public function getSeriesDateAttribute()
{
return self::seriesByDate();
//OR build 'seriesByDate' manually, returning whatever you like.
}
}

How to get data from Multiple Tables with Laravel Eloquent

I have 2 tables called jobs & job_records. Its relationship as below:
JobRecords Model
public function job()
{
return $this->belongsTo(Job::class);
}
Job Model:
public function jobRecord()
{
return $this->hasOne(JobRecord::class);
}
jobs table has 2 columns that I need to display alongside my job_records table view. It's total_pges & status.
In my JobRecords Controller, I have tried the following method. It throws me an error of Call to undefined relationship.
JobRecordController:
$job_records = JobRecord::whereStatus('In Progress')
->with('jobs', 'jobs.status', 'jobs.total_pges')
->get();
return DataTables::of($job_records)
I am still beginning with Laravel and PHP. I can sense that there is something wrong with the relationship. But I couldn't figure out what it is exactly. Can anyone help me out with this matter?
In your JobRecord model change the relation ship as
public function job()
{
return $this->hasOne('App\Models\Job','foreign_key','local_key');
}
Similarly, in Job model
public function job()
{
return $this->belongsTo('App\Models\JobRecord','foreign_key','local_key');
}
Replace foreign_key and local_key with appropriate values...
I deleted my previous answer. What are you trying to do exactly? You can't use "jobs" in the "with function" without to define "jobs" as function in the model.
If you change it to "job" (instead of "jobs), then it would work, but I don't know if you want this. With your query you saying that a record have many jobs? But your model doesn't define that.

How to define a relation between three tables and a junction table

Ok, I'm new to yii2 (and web development, as a matter of fact) so take it easy on me.
If I had a many-to-many relation between two tables, say, Environment and Category, and a junction table relEnvCat, I would have something like this:
public function getEnvironments()
{
return $this->hasMany(ENVIRONMENT::className(), ['PKENVIRONMENT' => 'FKENVIRONMENT'])
->viaTable('RELENVCAT', ['FKCATEGORY' => 'PKCATEGORY']);
}
But what should I do if I had three tables and a junction table. Exemple with their primary keys: CI(pkCi), Environment(pkEnvironment)and Context(pkContext).
Then I have a junction table, named relCiEnvCont with these foreign keys (fkCI, fkEnvironment, fkContext).
But, I don't know how to define this triple relation on yii... Can anybody help me?
As far as I know, which isn't much, all I can do is this:
class CI extends \yii\db\ActiveRecord
{
....
public function getRELCIENVCONTs()
{
return $this->hasMany(RELCIENVCONT::className(), ['FKCI' => 'PKCI']);
}
...
}
And that is not good at all... I'm thinking maybe these relations are far too complex for Active Records and I should ditch it and use query builder instead?

Add WHERE condition to all SQL requests in Laravel

I'm creating an online tool for companies that each have a set of users in Laravel.
When a user is connected, he has a $connected_company_id variable
For every SELECT request (called by ::all(), find(), ...), i would like to add the condition: where company_id = $connected_company_id. I have found this post: laravel set an automatic where clause, but it doesn't work by overriding newQuery().
For every INSERT request, i would like to add the company_id.
Is this possible without changing my code inside all the controllers ?
I thought about extending Eloquent with customEloquent, and then make my models extend customEloquent, but I don't know how to write the code for customEloquent and if it could work.
Well, you could make use of the Eloquent Model Events. I assume you have the connected_company_id stored in the Session company_id
class BaseModel extends Eloquent{
public static function boot(){
parent::boot();
//Column to inject when inserting
static::creating(function ($obj){
$obj->company_id = Session::get('company_id');
});
//Column to inject when updating
static::updating(function ($obj){
$obj->company_id = Session::get('company_id');
});
}
}
You can extend the BaseModel class on all the models that you want the company_id to be inserted or updated. Take a look at Eloquent Model Events for more information.
The above code will automatically insert or update the company_id to the model that you extend the BaseModel to. When you do a Model::all() or Model::get(), you automatically get the company_id on that Model and you can also perform searches as you requested on Point `
Hope this helps.
well, you can just add the company id to the find query.
Model::where("company_id","=",$company_id):
Or you can create a scope:
class theModel extends Eloquent {
static $company_id;
static for_company($company_id){
self::company_id=$company_id;
return __CLASS__;
}
public function scopeCompany($query)
{
return $query->where('company_id', '=', self::company_id);
}
}
//And later
$scope=theModel::for_company($company_id);
$res=$scope::company->where(...);
Disclaimer: I haven't tried this. Just a solution I constructed. Let me know if this works. This will not work under PHP 5.3