laravel one to Many Relationship with multiple table, query not optimized? - laravel

I have a table call table1(i am using some pretended name for tables here) like follow
id_table1 description
And Model is Like
`
class Table1 extends \Eloquent {
public $timestamps = false;
protected $table = 'table1';
protected $fillable = [ 'description'];
protected $primaryKey = 'id_table1';
public function relation5s(){
return $this->hasMany('Relation5','id_table1','id_table1');
}
public function relation4s(){
return $this->hasMany('Relation4','id_table1','id_table1');
}
public function relation3s(){
return $this->hasMany('Relation3','id_table1','id_table1');
}
public function relation2s(){
return $this->hasMany('Relation2','id_table1','id_table1');
}
public function relation1s(){
return $this->hasMany('Relation1','id_table1','id_table1');
}
}
This table is related to 5 tables names relation_table1,relation_table2 ... and so on.
Here is a sample model code for relation tables
<?php
use Illuminate\Database\Eloquent\Relations;
class Relation1 extends \Eloquent {
public $timestamps = false;
protected $table = 'relation_table1';
protected $fillable = [ 'id_table1','idReseller', 'login', 'password', 'type','etc'];
public function childs(){
return $this->hasMany('Relation2','id_relation','id');
}
public function table1(){
return $this->belongsTo('Table1','id_table1','id_table1');
}
public function scopeActive($query){
return $query->whereRaw('type % 2 = 1');
}
public function scopeInactive($query){
return $query->whereRaw('type % 2 = 0');
}
}
`
Problem is when i query below
$level=1; // i am controlling 5 relation tables in same controller via $level from route
$class = "Relation".$level;
$collection = new $class;
$results = $collection->with('Table1')->orderBy($sort,$order)->paginate();
its working showing all result from relation table .. but its doesn't solve n+1 query problem. i mean for every row from relation1 its querying each time on table1 .. any idea how i can solve this ?
thanks in advance

You code is correct, just replace "Table1" by "table1":
$results = $collection->with('table1')->orderBy($sort,$order)->paginate();

Take a look at Eager loading, you are using it with the with method so it will load them in the first query
$level=5
$class = "Relation".$level::with('Table1')->orderBy($sort,$order)->paginate();

Related

laravel 5.7 correct way of getting results on many to many relation

I'm learning laravel framework and I've a pretty straightforward question:
I have three tables:
Foods table:
foods
--------------
id | name
Ingredients table:
ingredients
---------------
id, | name
Pivot table:
food_ingredient
---------------
food_id | ingredient_id
In the models I have the "belongsToMany" methods:
Food Model:
public function ingredients(){
return $this->belongsToMany('App\Models\Ingredient');
}
Ingredient Model:
public function foods(){
return $this->belongsToMany('App\Models\Food');
}
FoodsController (index):
$foods = Food::all();
foreach($foods as $food){
$food->ingredients = Ingredient::join('food_ingredient','food_ingredient.id','=','ingredients.id')
->where('food_ingredient.food_id',$food->id)->get();
}
I want to get the ingredients information (just the name until now) of each food, and this query (the one is in the foreach loop) does it, but I'm not sure if I'm doing it in a right way. I'm not using the belongsToMany relations so...there must be something wrong in my understanding.
How could I get the names of the ingredients?
Until know just those names are ok, but I'll add a third table "allergens" and it's pivot table "allergen_ingredient" soon. Then, I'll have to get each food, their ingredients, and also the allergens of each ingredient. So that, I want to do it well from now.
Thank you in advance.
Food Model
class Food extends Model
{
protected $table = 'foods';
protected $primaryKey = 'food_id';
public $incrementing = TRUE;
public $timestamps = FALSE;
protected $fillable = [
'food_name'
];
public
function ingredients()
{
return $this->belongsToMany(Ingredient::class, 'food_ingredients', 'food_id', 'ingredient_id');
}
}
Ingredient Model
class Ingredient extends Model
{
protected $table = 'ingredients';
protected $primaryKey = 'ingredient_id';
public $incrementing = TRUE;
public $timestamps = FALSE;
protected $fillable = [
'ingredient_name'
];
public
function foods()
{
return $this->belongsToMany(Food::class, 'food_ingredients', 'ingredient_id', 'food_id');
}
}
and you can call it this way:
$foods = Food::all();
foreach( $foods as $food )
{
foreach( $food->ingredients as $ingredient )
{
echo $ingredient->ingredient_name . " <br>";
}
}
food_ingredients is the pivot table name

Laravel 5.3 belongsToMany return null, pivot data

I have a next models:
class Product extends Model{
protected $table = 'products';
public function payments()
{
return $this->belongsToMany('App\Payment', 'payment_product', 'product_id', 'payment_id')
}
}
class Payment extends Model{
protected $table = 'payments';
public function products(){
return $this->belongsToMany('App\Product');
}
}
Pivot table: payment_product(id, product_id, payment_id)
Eloquent
public function details($id){
$product = Product::with('payments')->find($id);
dd($product->payments); // return null
return view('products.details', ['product' => $product]);
}
I need to bring in a foreach, but $product->payments is null. Why is that ?
All the tables are not empty.
UPD
Sub query $this->belongsToMany('App\Payment', 'payment_product', 'product_id', 'payment_id');
Results:
try:
public function payments()
{
return $this->belongsToMany('App\Payment', 'payment_product', 'payment_id', 'product_id')
}
I think you have your FK in the wrong order.
In fact, I don't think you need to specify anything more than:
public function payments()
{
return $this->belongsToMany('App\Payment')
}
Looks like you are joining two tables based on unrelated fields.
When you do
return $this->belongsToMany('App\Payment', 'payment_product', 'product_id', 'payment_id')
your table products gets inner joined with payment_product on
products.product_id = payment_product.payment_id.
But I think these two columns are not the related keys. Instead, in your payment_product, join with the product_id in this table with products.product_id.
That might work.
Rename payments to payments_method:
public function payments_method(){
return $this->belongsToMany('App\Payment'); //or ('App\Payment', 'payment_product', 'payment_id', 'product_id')
}
$product = Product::find($id);
$product->payments_method; //this is work
This is magic:)

Relationship hasone trying to get property of non-object

I got a problem with relationship one to one mechanism for edit & update condition, so I have model Siswa and Telepon, with Telepon belongs to Siswa... here is the code
Siswa.php (model)
class Siswa extends Model
{
protected $table = 'siswa';
protected $fillable = [
'nisn',
'nama_siswa',
'tgl_lahir',
'jns_klmin'
];
protected $dates = ['tgl_lahir'];
public function getNamaSiswaAttribute($nama_siswa){
return ucwords($nama_siswa);
}
public function setNamaSiswaAttribute($nama_siswa){
$this->attributes['nama_siswa']=ucwords($nama_siswa);
}
public function telepon(){
return $this->hasOne('App\Telepon', 'id_siswa');
}
}
Telepon.php (model)
class Telepon extends Model
{
protected $table = 'telepon';
protected $primKey = 'id_siswa';
protected $fillable = [
'id_siswa',
'no_telepon',
];
public function siswa(){
return $this->belongsTo('App\Siswa', 'id_siswa');
}
}
Edit and update function controller shown as follows :
public function edit($id){
$siswa = Siswa::findOrFail($id);
$siswa->no_telepon = $siswa->telepon->no_telepon;
return view('siswa.edit', compact('siswa'));
}
public function update(Request $request, $id){
$siswa = Siswa::findOrFail($id);
$input = $request->all();
$validator = Validator::make($input, [
'nisn'=>'required|string|size:4|unique:siswa,nisn,'.$request->input('id'),
'nama_siswa'=>'required|string|max:30',
'tgl_lahir'=>'required|date',
'jns_klmin'=>'required|in:L,P',
'no_telepon'=>'sometimes|numeric|digits_between:10,15|unique:telepon,no_telepon,'.$request->input('id').',id_siswa',
]);
if ($validator->fails()) {
return redirect('siswa/'.$id.'/edit')->withInput()->withErrors($validator);
}
$siswa->update($request->all());
$telepon = $siswa->telepon;
$telepon->no_telepon = $request->input('no_telepon');
$siswa->telepon()->save($telepon);
return redirect('siswa');
}
I got Trying to get property of non-object error in edit function, line "$siswa->no_telepon = $siswa->telepon->no_telepon;".
When we call edit view inside edit controller, it will give a form which inside of it has previous saved data. no_telepon itself is a column from Telepon table, not Siswa, so how to show telephone saved data for editing purposes is the problem. FYI, create works just fine, and no_telepon data saved in Telepon table. So, how about this one? Any help appreciated.
It's probably because you don't have any 'App\Telepon' in the database with 'id_siswa' pointing to the id of the record from table siswa.
You can ommit this error in this way:
public function edit($id){
$siswa = Siswa::findOrFail($id);
$siswa->no_telepon = $siswa->telepon ? $siswa->telepon->no_telepon : '';
return view('siswa.edit', compact('siswa'));
}

How to use "select" method to reduce data transfer when using Eager Loading

I have a API and its taking long time to get all the info and its because I'm only hidding some data but I want to omit not to hidde. I found select() method to chose wich data send and reduce the time to query all information I really need.
Im trying to use select just after the relation just like this, just to retrieve only name from OPR_User table:
public function creatorUser() {
return $this->belongsTo('Knotion\OPR_User', 'idCreatorUser', 'idUser')->select('name');
}
but is not working
This is my Model code
<?php
namespace Knotion;
use Illuminate\Database\Eloquent\Model;
class CTL_Resource extends Model {
protected $table = "CTL_Resource";
protected $primaryKey = "idResource";
public $incrementing = false;
public $timestamps = false;
public static $snakeAttributes = false;
protected $hidden = [
'coachVisibility', 'thumbnail',
'studentVisibility', 'isHTML','studentIndex', 'coachIndex',
'isURL', 'source', 'path', 'status', 'updateTime', 'isfolder',
'parentResource', 'idModifierUser', 'idResourceType', 'idCreatorUser', 'idCreationCountry'
];
protected $fillable = ['idResourceType','productionKey', 'idCreatorUser', 'idModifierUser', 'idCreationCountry', 'title', 'description', 'URL', 'fileName', 'extension', 'minimumAge', 'maximumAge', 'productionKey'];
public function creatorUser() {
return $this->belongsTo('Knotion\OPR_User', 'idCreatorUser', 'idUser');
}
public function creationCountry() {
return $this->belongsTo('Knotion\CTL_Country', 'idCreationCountry', 'idCountry');
}
public function resourceType() {
return $this->belongsTo('Knotion\CTL_ResourceType', 'idResourceType', 'idResourceType');
}
public function quickTags() {
return $this->belongsToMany('Knotion\CTL_QuickTag', 'CTL_Resource_has_QuickTags', 'idResource','idQuickTag');
}
public function tags() {
return $this->belongsToMany('Knotion\CTL_Tag','CTL_Resource_has_Tags', 'idResource', 'idTag');
}
public function relatedTo() {
return $this->belongsToMany('Knotion\CTL_RelatedTo', 'CTL_Resource_has_RelatedTo', 'idResource', 'idRelatedTo');
}
}
this is my relation model code (just in case needed):
<?php
namespace Knotion;
use Illuminate\Database\Eloquent\Model;
class OPR_User extends Model {
protected $table = "OPR_User";
protected $primaryKey = "idUser";
public $incrementing = false;
public $timestamps = false;
public static $snakeAttributes = false;
protected $hidden = ['firstName', 'secondName', 'firstSurName', 'secondSurName', 'password', 'picture', 'status', 'createTime', 'updateTime', 'idUserType', 'email'];
public function resources() {
return $this->hasMany('Knotion\CTL_Resource', 'idResource');
}
public function userType() {
return $this->belongsTo('Knotion\CTL_UserType', 'idUserType', 'idUserType');
}
}
and this is my Controller code:
public function index(Request $request) {
$resources = CTL_Resource::all();
$resources->resourceType->select('name');
return $resources->load('creatorUser', 'creationCountry', 'resourceType', 'tags', 'quickTags', 'relatedTo');
}
When you add the ->select after the ->belongsTo it's no longer an actual relationship type, it's a query builder. You need to add the select afterwards before you call the ->load.
To fix the problem I had to include the id also in the relation, something like this:
public function resources() {
return $this->hasMany('Knotion\CTL_Resource', 'idResource')->select('idResource', 'name');
}

Populate laravel object with relationships

I want to populate Every CustomerEvent with the Customer related to the CustomerEvent.
When I Loop through the object and call to $customerevent->customer foreach object I can get the customer related to that event but Can I populate the all objects in the main object without looping through every single event ?
I want to do something like this:
$typeOne = CustomerEvent::typeOne()->get();
$customersWithTypeOne = $typeOne->Customers;
Here my code:
Table 1: "events"
id, customer_id
Model for Table 1 "CustomerEvent":
<?php
class CustomerEvent extends Eloquent{
protected $guarded = ['id'];
public $table = "event";
protected $softDelete = true;
public function scopeTypeOne($query)
{
$followUps = $query->where('type_id', '=', '1');
}
public function Customers()
{
return $this->belongsTo('Customer', 'customer_id');
}
}
Table 2: "customers"
id
Model for Table 2 "Customers":
<?php class Customer extends BaseModel{
protected $guarded = ['id'];
}
EventController:
public function index()
{
$typeOne = CustomerEvent::typeOne()->get();
$customersWithTypeOne = $typeOne->Customers;
dd($customersWithTypeOne);
}
From you database scheme I see customer has many events, so I would recommend to define this relationship in you Customer model.
class Customer extends BaseModel{
protected $guarded = ['id'];
public function events()
{
return $this->hasMany('CustomerEvent', 'customer_id');
}
}
Then you will be able to query customers with events:
$customersWithTypeOne = Customer::whereHas('events', function($query){
$query->where('type_id', 1);
})->get()
Maybe Ardent can help you: https://github.com/laravelbook/ardent
It's an extension for Eloquent models and very popular.

Resources