I want to join this 3 tables.
delegations
delegations_id
delegation
delegates
delegates_id
delegatesname
delegations_id
meals
delegates_id
$meals = Meal::join('delegates', 'delegates.delegates_id', '=', 'meals.delegates_id')
->join('delegates', 'delegates.delegations_id', '=', 'delegates.delegations_id')
->get();
write following code in controller
use DB;
write following query inside of controller function
$meals=DB::select("SELECT delegations .*, delegates .*, meals.*
FROM delegations
JOIN delegates
ON delegates.delegations_id = delegations.delegations_id
JOIN meals
ON meals.delegates_id = delegates.delegates_id");
Why would you want to do it "manually" if your using laravel? check on this reference eloquent-relationship
in that case just define in models (these are concept examples, I cant be sure about your project structure):
Meal
function delegate(){
$this->belongsTo(Delegate::class);
}
Delegate
function delegation(){
$this->belongsTo(Delegate::class);
}
function meal(){
$this->hasOne(Meal::class);
}
Delegation
function delegates(){
$this->hasMany(Delegate::class);
}
and then you could just do:
$meals = Meal::all();
foreach ($meals as $meal){
$delegates = $meal->delegate;
$delegation = $mealt->delegate->delegation;
}
you can use it on the controller o on blades and then you'll have all the info you need for instance $delegate->name, $delegation->id an so on
You can do it by two ways:
1.- Raw querys
use DB;
public static function getRaw_data(){
return DB::select("SELECT d1.*, d2.*, m.*
FROM delegations d1, delegates d2, meals m
WHERE d1.delegations_id = d2.delegations_id AND d2.delegates_id = m.delegates_id );
}
2.-
use DB;
public static function getRaw_data(){
return DB::table('delegations')
->join('delegates','delegations.delegations_id','=','delegates.delegations_id')
->join('meals','delegates.delegates_id','=','meals.delegates_id')
->select('delegations.*','delegates.*','meals.*')
->get();
}
Related
I have a relationship between your_electricity_yesterday_category and building as building_id is present in your_electricity_yesterday_category table.
I am trying to get details out of the building table using the relationship.
I have this in my Electricity model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Electricity extends Model
{
use HasFactory;
protected $connection = 'mysql2';
protected $table = 'your_electricity_yesterday_category';
public function buildings()
{
return $this->belongsTo(Building::class, 'building_id');
}
}
I have this in my Repository
public function getAllBuilding()
{
// $buildings = Building::where('module_electricity', 1)->orderBy('description')->get();
$buildings = Electricity::with('buildings')->get();
return $buildings;
}
I have this in my controller
public function electBuilding()
{
$getBuilding = $this->electricityRepository->getAllBuilding();
return response()->json($getBuilding);
}
On the building table i have a column where module_electricity is either 0 or 1
How can i use this relationship to return building where module_electricity is 1 in json?
use whereHas query builder to filter parent Electricity details based on condition
$buildings = Electricity::with(['buildings'=>function($query){
$query->where('module_electricity',1);
}])
->whereHas('buildings',function($query){
$query->where('module_electricity',1);
})->get();
Also you can write scope for where condition in buildings model like below
public function scopeModuleElectricity($query,$module){
return $query->where('module_electricity',$module);
}
so your query will be
$buildings = Electricity::with(['buildings'=>function($query){
$query->moduleElectricity(1);
}])
->whereHas('buildings',function($query){
$query->moduleElectricity(1);
})->get();
Here is what I came up with:
public function getAllBuilding()
{
return Electricity::query()
->with('buildings', fn ($query) => $query->where('module_electricity', 1))
->get()
->pluck('buildings')
->collapse();
}
Walking through this step by step so you can better understand what's happening:
Initiating a query (completely optional, just for better code formatting)
Eager loading buildings with a condition (module_electricity = 1)
Retrieving data from the database
Extracting buildings only
Flat-mapping results
This will return a single collection with buildings that met a condition.
Let me know if the result turned out to be exactly what you expected.
P.S. Note that the above solution might not work if you're using older versions of PHP. If the above returns syntax error:
replace:
fn ($query) => $query->where('module_electricity', 1)
with:
function ($query) {
$query->where('module_electricity', 1)
}
I have four models created, and their relations have been defined correctly. What I haven't grasped is how to use those relations for my situation.
Let's use countries > regions > cities > censuses as a contrived example of the relations. I can get the most recent date a census was taken on with this function in the Census model:
public static function getMaxTakenOn($country_id)
{
return \DB::table('regions')
->join('cities', 'cities.region_id', '=', 'region.id')
->join('censuses', 'censuses.city_id', '=', 'cities.id')
->where('regions.country_id', '=', $country_id)
->max('censuses.taken_on');
}
How would I rewrite the function body to use the Eloquent Relations?
Using the hasMany and a custom field in each of the models:
https://laravel.com/docs/5.5/eloquent-relationships
Example in your Regions model, you will need a:
public function cities() {
return $this->hasMany('App\Cities');
}
Then so on in your other models.
Once that is done, you don't need the 2 joins, you can access them like:
$region = new Region();
$region->where('country_id', '=', $country_id)
->max('censuses.taken_on');
echo $region->cities()->censuses()->count();
I have a working query:
Object::all()->with(['reviews' => function ($query) {
$query->where('approved', 1);
}])
And I want to limit the number of reviews, per object, that is returned. If I use:
Object::all()->with(['reviews' => function ($query) {
$query->where('approved', 1)->take(1);
}])
or
Object::all()->with(['reviews' => function ($query) {
$query->where('approved', 1)->limit(1);
}])
it limits the total number of reviews, where I want to limit the reviews that are returned by each object. How can I achieve this?
Eloquent way
Make one relationship like below in your model class
public function reviews() {
return $this->hasMany( 'Reviews' );
}
//you can pass parameters to make limit dynamic
public function firstReviews() {
return $this->reviews()->limit( 3 );
}
Then call
Object::with('firstReviews')->get();
Faster way(if you just need one review)
Make a derived table to get the latest review 1st and then join it.
Object->select('*')
->leftJoin(DB::raw('(SELECT object_id, reviews FROM reviews WHERE approved=1 ORDER BY id DESC limit 0,1
as TMP'), function ($join) {
$join->on ( 'TMP.object_id', '=', 'object.id' );
})
->get();
Grabbing 1 child per parent
You can create a helper relation to handle this very easily...
In your Object model
public function approvedReview()
{
return $this->hasOne(Review::class)->where('approved', 1);
}
Then you just use that instead of your other relation.
Object::with('approvedReview')->get();
Grabbing n children per parent
If you need more than 1, things start to become quite a bit more complex. I'm adapting the code found at https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/ for this question and using it in a trait as opposed to a BaseModel.
I created a new folder app/Traits and added a new file to this folder NPerGroup.php
namespace App\Traits;
use DB;
trait NPerGroup
{
public function scopeNPerGroup($query, $group, $n = 10)
{
// queried table
$table = ($this->getTable());
// initialize MySQL variables inline
$query->from( DB::raw("(SELECT #rank:=0, #group:=0) as vars, {$table}") );
// if no columns already selected, let's select *
if ( ! $query->getQuery()->columns)
{
$query->select("{$table}.*");
}
// make sure column aliases are unique
$groupAlias = 'group_'.md5(time());
$rankAlias = 'rank_'.md5(time());
// apply mysql variables
$query->addSelect(DB::raw(
"#rank := IF(#group = {$group}, #rank+1, 1) as {$rankAlias}, #group := {$group} as {$groupAlias}"
));
// make sure first order clause is the group order
$query->getQuery()->orders = (array) $query->getQuery()->orders;
array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']);
// prepare subquery
$subQuery = $query->toSql();
// prepare new main base Query\Builder
$newBase = $this->newQuery()
->from(DB::raw("({$subQuery}) as {$table}"))
->mergeBindings($query->getQuery())
->where($rankAlias, '<=', $n)
->getQuery();
// replace underlying builder to get rid of previous clauses
$query->setQuery($newBase);
}
}
In your Object model, import the trait use App\Traits\NPerGroup; and don't forget to add use NPerGroup right under your class declaration.
Now you'd want to setup a relationship function to use the trait.
public function latestReviews()
{
return $this->hasMany(Review::class)->latest()->nPerGroup('object_id', 3);
}
Now you can use it just like any other relationship and it will load up the 3 latest reviews for each object.
Object::with('latestReviews')->get();
Table A = Inventory | Table B = ItemAssociation | Table C = ItemValue
I have Table A, B and C. A and B have a one-to-one relationship, B and C have a one to one relationship. I'm currently using the HasManyThrough relationship to arrive at this:
public function item(){
return $this->hasManyThrough('App\ItemValue','App\ItemAssociation','id','id');
}
And in my controller:
public function orm(){
$inventory = Inventory::getAssocBySteamID(76561198124900864)->get();
$i = 0;
foreach($inventory as $inv){
$this->data[$i] = $inv->item()->get();
$i++;
}
return $this->data;
}
Where Inventory::getAssocBySteamID:
public static function getAssocBySteamID($id){
return SELF::where('steamid64','=',$id);
}
This returns all the data I need, however, I need to order this by a column in Table C, the ItemValue model.
Any help would be greatly appreciated.
You can add ->orderBy('TableName', 'desc') to the getAssocBySteamID function.
Or to the query in the Orm() function before the ->get()
Also for Where clauses in Eloquent's QB you don't need the "="sign. You can just do where('steamid',$id)
Do not use static function use scopes. Try using join query like this:
// Inventory model method
public function scopeAssocBySteamID($query, $id) {
$query->where('steamid64', $id)
->join('ItemValue'. 'ItemValue.id', '=', 'Inventory.ItemValue_id')
->select('Inventory.*')
->orderBy('ItemValue.item_price')
}
And then:
public function orm(){
$inventory = Inventory::assocBySteamID(76561198124900864)->get();
$i = 0;
foreach($inventory as $inv){
$this->data[$i] = $inv->item()->get();
$i++;
}
return $this->data;
}
Check all table and field names befor testing this example.
In my Database, I have:
tops Table
posts Table
tops_has_posts Table.
When I retrieve a top on my tops table I also retrieve the posts in relation with the top.
But what if I want to retrieve these posts in a certain order ?
So I add a range field in my pivot table tops_has_posts and I my trying to order by the result using Eloquent but it doesn't work.
I try this :
$top->articles()->whereHas('articles', function($q) {
$q->orderBy('range', 'ASC');
})->get()->toArray();
And this :
$top->articles()->orderBy('range', 'ASC')->get()->toArray();
Both were desperate attempts.
Thank you in advance.
There are 2 ways - one with specifying the table.field, other using Eloquent alias pivot_field if you use withPivot('field'):
// if you use withPivot
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range');
}
// then: (with not whereHas)
$top = Top::with(['articles' => function ($q) {
$q->orderBy('pivot_range', 'asc');
}])->first(); // or get() or whatever
This will work, because Eloquent aliases all fields provided in withPivot as pivot_field_name.
Now, generic solution:
$top = Top::with(['articles' => function ($q) {
$q->orderBy('tops_has_posts.range', 'asc');
}])->first(); // or get() or whatever
// or:
$top = Top::first();
$articles = $top->articles()->orderBy('tops_has_posts.range', 'asc')->get();
This will order the related query.
Note: Don't make your life hard with naming things this way. posts are not necessarily articles, I would use either one or the other name, unless there is really need for this.
For Laravel 8.17.2+ you can use ::orderByPivot().
https://github.com/laravel/framework/releases/tag/v8.17.2
In Laravel 5.6+ (not sure about older versions) it's convenient to use this:
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range')->orderBy('tops_has_posts.range');
}
In this case, whenever you will call articles, they will be sorted automaticaly by range property.
In Laravel 5.4 I have the following relation that works fine in Set model which belongsToMany of Job model:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
The above relation returns all jobs that the specified Set has been joined ordered by the pivot table's (eqtype_jobs) field created_at DESC.
The SQL printout of $set->jobs()->paginate(20) Looks like the following:
select
`jobs`.*, `eqtype_jobs`.`set_id` as `pivot_set_id`,
`eqtype_jobs`.`job_id` as `pivot_job_id`,
`eqtype_jobs`.`created_at` as `pivot_created_at`,
`eqtype_jobs`.`updated_at` as `pivot_updated_at`,
`eqtype_jobs`.`id` as `pivot_id`
from `jobs`
inner join `eqtype_jobs` on `jobs`.`id` = `eqtype_jobs`.`job_id`
where `eqtype_jobs`.`set_id` = 56
order by `pivot_created_at` desc
limit 20
offset 0
in your blade try this:
$top->articles()->orderBy('pivot_range','asc')->get();
If you print out the SQL query of belongsToMany relationship, you will find that the column names of pivot tables are using the pivot_ prefix as a new alias.
For example, created_at, updated_at in pivot table have got pivot_created_at, pivot_updated_at aliases. So the orderBy method should use these aliases instead.
Here is an example of how you can do that.
class User {
...
public function posts(): BelongsToMany {
return $this->belongsToMany(
Post::class,
'post_user',
'user_id',
'post_id')
->withTimestamps()
->latest('pivot_created_at');
}
...
}
You can use orderBy instead of using latest method if you prefer. In the above example, post_user is pivot table, and you can see that the column name for ordering is now pivot_created_at or pivot_updated_at.
you can use this:
public function keywords() {
return $this->morphToMany(\App\Models\Keyword::class, "keywordable")->withPivot('order');
}
public function getKeywordOrderAttribute() {
return $this->keywords()->first()->pivot->order;
}
and append keyword attribiute to model after geting and use sortby
$courses->get()->append('keyword_order')->sortBy('keyword_order');