I have a noob question about how Eloquent generates SQL for the following.
DB:
Customer Table:
ID (Public Key)
... (Some general columns)
Group Table:
ID (Public Key)
FK_CUSTOMER_ID (Foreign Key)
... (Some general columns)
I have the following code in Customer Model:
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
I am trying to get all groups (in the groups function above), that I can narrow down the groups later, to a particular customer.
The above code generates the following SQL (which results an empty result set, which is understandable by looking at the SQL). I have no idea, why the where clause (see the SQL below) gets generated, does not makes much sense.
select * from `group` where `group`.`fk_customer_id` is null and `group`.`fk_customer_id` is not null limit 1
I would like the following SQL to be generated :
select * from `group`
also, how to get the following SQL generated (If I need to select groups based on customer_id, I believe I'll need to add some where clause somehow)
select * from `group` where `group`.`fk_customer_id`= SOME_VALUE
Thanks!
--- Customer Model
<?php
namespace App;
use App\Role;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
/**
* Get ALL groups for the customer.
*/
public function groups()
{
return $this->hasMany(Group::class, 'fk_customer_id');
}
/**
* Get ONE group for the customer.
*/
public function group($groupId)
{
return $this->hasMany(Group::class, 'fk_customer_id')->where('id', $groupId);
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'customer';
}
Group Model
<?php
namespace App;
use App\Group;
use App\Customer;
use Illuminate\Database\Eloquent\Model;
class Group extends Model
{
/**
* Get ONE customer for the group.
*/
public function customer()
{
return $this->belongsTo(Customer::class, 'fk_customer_id');
}
/**
* Get ONE directory configuration for the group.
*/
public function directoryConfiguration()
{
return $this->belongsTo(DirectoryConfiguration::class, 'fk_directory_configuration_id');
}
/**
* Get ONE user for the group.
*/
public function user($userId)
{
return $this->hasMany(User::class, 'fk_role_id')->where('user_id', $userId);
}
/**
* Get ALL user for the group.
*/
public function users()
{
return $this->hasMany(User::class, 'fk_role_id');
}
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'group';
}
Related
I have an eloquent model "Athlete" and there is another table performances. Each athlete has 0 to many performances. And I would like get best performance of each athlete(personal best) or null if the athlete doesnt have any performances yet.
My athlete model:
class Athlete extends Model
{
// I would like to do something like
public $personalBest = max(performances) - the highest perfomance
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'athletes';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* Get the performances for the Athelete post.
*
* #return HasMany
*/
public function performances()
{
return $this->hasMany('App\EloquentModels\Performance', 'athlete_id', "id");
}
}
I would like to get the highest performance of each athlete. Hope it does make sense.
I think it has to be answered somewhere but I had no luck finding it. So sorry if I just failed to find it.
Performances table
id(int) year(int) performance(float)
-------------------------------------
1 2000 257.3
2 2001 227.3
Just to wrap things up. Posting the final raw query which was generated:
select [athletes].[first_name], [athletes].[last_name], MAX(performance) AS personal_best
from [athletes]
left join [performances] on [athletes].[id] = [performances].[athlete_id]
group by [athletes].[id], [athletes].[first_name], [athletes].[last_name]
order by [personal_best] desc
Using withCount should do the job
$athletes= App\Athlete::withCount('performances')->get();
foreach ($athletes as $athlete) {
echo $athlete->performances_count;
}
If you want max performance, you can do something like
$athletes= App\Athlete::all();
foreach ($athletes as $athlete) {
echo $athlete->performances->pluck('performance')->max();
}
Something like
select e.athelete.id, max(perf.performace) as max_performace
from atheletes ath
left join performaces perf on ath.id = perf.athelete_id
group by ath.id, max_performace
may be something like
DB('athletes as ath')::leftJoin('performaces as perf', 'ath.id', 'perf.athelete_id')->select(e.athelete.id, max(perf.performace) as max_performace)->orderBy('max_performace');
You can use order by max_performace, if you need.
I think you can also use simply
echo $athlete->performances->max('performance');
I have a One to Many relationship between two classes. When I try to receive the belongsTo child via eager loading, it doesn't work for some odd reason. According to the following it should work: https://laravel.com/docs/master/eloquent-relationships#eager-loading
Te tables have the following columns, among others (legacy made, no option to change this). kasticket_.kassa_id and kassa.code refer to each other:
kasticket_.ticketnr, kasticket_.kassa_id, ...
kassa.code, ...
Here are the classes, somewhat simplified.
ReceiptDetail:
<?php
namespace App;
use App\Pos;
use Illuminate\Database\Eloquent\Model;
class ReceiptDetail extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'kasticket_';
/**
* The primary key for the model.
*
* #var string
*/
protected $primaryKey = 'ticketnr';
/**
* Get pos.
*/
public function pos()
{
return $this->belongsTo(Pos::class, 'kassa_id', 'code');
}
}
Pos:
<?php
namespace App;
use App\ReceiptDetail;
use Illuminate\Database\Eloquent\Model;
class Pos extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'kassa';
/**
* The primary key for the model.
*
* #var string
*/
protected $primaryKey = 'code';
/**
* Get the receipt details.
*/
public function receiptDetails()
{
return $this->hasMany(ReceiptDetail::class, 'kassa_id', 'code');
}
}
This gives null as a result, rather than the Pos model. In the query log I can see it gets eager loaded however:
$receipts = \App\ReceiptDetail::with('pos')->get();
foreach ($receipts as $receipt) {
dd($receipt->pos);
}
This gives me the expected Pos model, but an extra DB request has been made:
$receipts = \App\ReceiptDetail::with('pos')->get();
foreach ($receipts as $receipt) {
dd($receipt->pos()->first());
}
From the query log I see the following:
select * from `kasticket`
select * from `kassa` where `kassa`.`code` in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
The unique identifiers of the tables (kasticket_.kassa_id and kassa.code) are strings in the DB. I had to add the following line the the Pos class:
/**
* Indicates if the IDs are auto-incrementing.
*
* #var bool
*/
public $incrementing = false;
If you don't follow the proper name in primary key and foreign key then you need to add the relationship properly
Change like this:
public function pos()
{
return $this->belongsTo(Pos::class,'foreign_key', 'local_key');
}
POS Model
public function receiptDetails()
{
return $this->hasMany(ReceiptDetail::class,'foreign_key', 'local_key');
}
More info
I am trying to query one of my model relationships in a timesheet system I am building. I have the following models setup:
User can have many timesheets and can belong to many employee types
Timesheet can have many rows
The models are setup like so:
User Model:
<?php namespace App\Models\User;
....
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
/**
* The employee types that belong to the user.
*
* #return Object
*/
public function employeeTypes()
{
return $this->belongsToMany('App\Models\User\EmployeeType')->withTimestamps();
}
Timesheet Model:
<?php namespace App\Models\Timesheet;
....
class Timesheet extends Model
{
/**
* The user that owns the timesheet.
*
* #return Object
*/
public function user()
{
return $this->belongsTo('App\Models\User\User');
}
My question is, how can I query the User relationship and get timesheets by employee_type. This means I need to access timesheet table, then get users associated with the timesheet and then get the users by an employee type that I specify.
I have tried the following...
$timesheets->with('user')->whereHas('employeeTypes', function ($query) use ($request) {
$query->where('name', 'my_employee_type');
});
...but it gives an error...
Error:
Call to undefined method Illuminate\Database\Query\Builder::employeeTypes()
Does anyone know how I achieve this?
Timesheets doesn't have that method. That method (relationship) exists on User, not Timesheet.
You can try something like this and see if it would work.
$timesheets = Timesheet::whereHas('user.employeeTypes', function ($q) {
$q->where('name', 'something');
})->get();
MySQL Tables:
- category
- unit
- category_unit (many to many)
- category_id
- unit_id
Laravel 5 Models:
<?php
class Unit extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'unit';
}
class Category extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'category';
public function units()
{
return $this->morphToMany('App\Unit', 'category_unit'); // Table not in plural.
}
}
Controller Code:
$category = Category::find($id);
var_dump($category->units);
Error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.category_units' doesn't exist (SQL: select `unit`.*, `category_units`.`category_unit_id` as `pivot_category_unit_id`, `category_units`.`unit_id` as `pivot_unit_id` from `unit` inner join `category_units` on `unit`.`id` = `category_units`.`unit_id` where `category_units`.`category_unit_id` = 1 and `category_units`.`category_unit_type` = App\Category)
Laravel 5 is trying to find the table category_unit as the plural category_units. As my database is not new and I already used it in production servers, I cannot change the table name.
How can I do to Laravel 5 use it with singular name?
The problem here is that you are trying to create Many to Many relationship using a polymorphic one.
The morphToMany() method doesn't take the table name as the second argument. I think your case is simpler, just change the relation to belongsToMany()
So your code should be
class Category extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'category';
public function units()
{
return $this->belongsToMany('App\Unit', 'category_unit'); // Table not in plural.
}
}
I want to make a relation with 3 table using ORM but cant. My tables
User table
id | userame | name |
1 Ellie Elen
2 Pol Paul
record table
id | user_id| item_id| hour|
1 2 1 3
2 2 2 5
Item table table
id | title
1 item 1
2 item 2
I am using this logic but not work properly
class User Extends Eloquent {
public function record()
{
return $this->hasMany('VolunteerRecord');
}
}
class VolunteerRecord Extends Eloquent {
function item() {
return $this->hasMany('VolunteerItem');
}
}
I cant understand how to do it?
It seems like you want a Many-To-Many relationship between Users and Items but you also want to track hours on the pivot table. So first, you'll define the many-to-many relationships using belongsToMany(), and you'll tell Laravel that you have extra data on your pivot table with the withPivot() function. Your classes will look like this:
class User extends Eloquent {
protected $table = 'users';
public function items() {
return $this->belongsToMany('Item', 'records')->withPivot('hour');
}
}
class Item extends Eloquent {
protected $table = 'items';
public function users() {
return $this->belongsToMany('User', 'records')->withPivot('hour');
}
}
Then, to access the hour field you would do this:
$user = User::first(); // First User
$item = $user->items()->first(); // User's first Item
$hour = $item->pivot->hour; // The 'hour' on the User-Item relationship
Also, your current column naming scheme is correct for Laravel so don't feel like you need to change it. If you change your column names, then you'll need to define them in the belongsToMany() method like this:
$this->belongsToMany('ModelName', 'pivot_table', 'foreign_key', 'other_key');
// For example, in Items::users() you would have this:
$this->belongsToMany('User', 'records', 'users_id', 'items_id');
Finally, I'm assuming that your tables are named users, items, and records. If they are not, then just replace all instances of users, items, and records with your actual table names.
Based on your table names, I'd suggest the following, first of all, change your record table as follows:
id | users_id| items_id| hour|
1 2 1 3
2 2 2 5
And these are the classes for your models:
class Users extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
public function records()
{
return $this->hasMany('Records');
}
}
class Records extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'records';
public function record()
{
return $this->hasOne('Users');
}
public function item()
{
return $this->hasOne('Items');
}
}
class Items extends Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'items';
public function records()
{
return $this->hasMany('Records');
}
}
These contain the relations for your models.
If you were to select a few records, for each record you can get the user and the item. If you were to select an item, and all records for that item. You can also get the user for each record.
In User Model
public function images()
{
return $this->belongsToMany('Item')->withPivot('hour');
}
In user controller
public function view($username)
{
$user = User::where('name',$username)->firstOrFail();
return View::make('view')->with('user',$user);
}
In view
#foreach ($users->items as $item)
name: {{$image->title}}<br>
hour: {{$image->pivot->hour}}<br>
#endforeach