Laravel 5 hasManyThrough Pivot Table - eloquent

I am trying to create a relationship to access a table called comments through a model called grade, loaded through the students in the grade
Both the Grade and Student models belongToMany of the other
From my understanding, it is not possible to access a hasManyThrough relationship that requires a pivot table (comments do not have a Grade identifier, only a student identifier)
class Grade extends Model{
public function comments(){
return $this->hasManyThrough("App\Comment","App\Student");
}
}
I have these functions I found for Laravel 4 # HasManyThrough with one-to-many relationship but it gives me the error Class 'App\Illuminate\Database\Eloquent\Relations\HasMany' not found
I don't have a good understanding on Namespaces, and can't work out what I should be doing in it's place for Laravel 5.
public function getCommentsAttribute()
{
if ( ! array_key_exists('comments', $this->relations)) $this->loadComments();
return $this->getRelation('comments');
}
protected function loadComments()
{
$comments = Comment::join('grade_student', 'comments.student_id', '=', 'grade_student.student_id')
->where('grade_student.grade_id', $this->getKey())
->distinct()
->get(['comments.*','grade_id']);
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(Translation::query(), $this, 'grade_id', 'id');
$hasMany->matchMany(array($this), $comments, 'comments');
return $this;
}
More Info
Comment Table
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('student_id')->unsigned();
$table->integer('domain_id')->unsigned();
$table->integer('teacher_id')->unsigned();
$table->text('comment');
$table->foreign('student_id')->references('id')->on('students') ->onDelete('cascade');
$table->foreign('domain_id') ->references('id')->on('domains')->onDelete('cascade');
$table->foreign('teacher_id')->references('id')->on('teachers')->onDelete('cascade');
});
My Students Table
Schema::create('students', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('unique_identifier');
$table->string('first_name');
$table->string('last_name');
$table->enum('gender',array('m','f'));
});
My Grades Table
Schema::create('grades', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('name',20);
$table->integer('school_id')->unsigned();
$table->integer('level_id')->unsigned();
$table->foreign('school_id')->references('id')->on('schools')->onDelete('cascade');
$table->foreign('level_id')->references('id')->on('classes_levels')->onDelete('cascade');
});
My Pivot Table
Schema::create('grade_student', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->timestamps();
$table->integer('grade_id')->unsigned();
$table->integer('student_id')->unsigned();
$table->integer('school_id')->unsigned();
$table->integer('year');
$table->foreign('grade_id')->references('id')->on('grades')->onDelete('cascade');
$table->foreign('student_id') ->references('id')->on('students')->onDelete('cascade');
$table->foreign('school_id')->references('id')->on('schools') ->onDelete('cascade');
});

Take a look at here.
At the moment Laravel 5.3 doesn't support hasManyThrough using pivot table. Just use an alternative ways.
As an alternative way:
Just consider these models and their relations:
A <=> B with pivot table A_B
B <=> C with pivot table B_C
Now you can create a pivot VIEW call A_C:
SELECT A_id, C_id FROM A_B
INNER JOIN B_C on A_B.B_id = B_C.B_id
GROUP BY A_id, C_id
I think you can guess my trick.
Yes, forget about hasManyThrough.
Now you can use A.belongsToMany(C).

Related

Laravel eloquant belongsToMany get records belongs to relationship

I'm using many to many relationship with products and product_categories tables using product_product_category pivot table.
Product model
class Product extends Model
{
public function product_categories() {
return $this->belongsToMany(ProductCategory::class, 'product_product_category');
}
}
ProductCategory model
class ProductCategory extends Model {
public function products() {
return $this->belongsToMany(Product::class, 'product_product_category');
}
}
What I need to do is when I supply an array of categories need to get products only with these categories. This is my code
$selectedCategotries = array(1, 2);
$products = Product::with(['product_categories' => function($q) use ($selectedCategotries){
$q->whereIn('product_categories.id', $selectedCategotries);
}])->get();
But I get all the products instead. It will be a great help if you can supply a solution for me.
Finally, I found an answer with whereHas. Adding the answer for anyone who come up with the same issue.
$products = Product::whereHas('product_categories', function ($q) use ($selectedCategotries) {
$q->whereIn('product_categories.id', $selectedCategotries);
})->get();

Single model using many-to-many relationship with several other models

I want to have a single table containing information about people (name, phone, email) called contacts that can then be associated with several other tables (clients, properties, jobs). However, I have no clue how to approach this through Eloquent. I considered having 2 additional fields in the contacts table, an entity_id and an entity_type to identify the associated records in the other tables, but I am hoping there is a cleaner, more Laravel way to accomplish this.
create_contacts_table.php
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('phone')->nullable();
$table->string('email')->nullable();
$table->timestamps();
});
create_clients_table.php
Schema::create('clients', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('address_1');
$table->string('address_2')->nullable();
$table->string('city')->nullable();
$table->string('state')->nullable();
$table->integer('zip_code')->nullable();
$table->timestamps();
});
create_properties_table.php
Schema::create('properties', function (Blueprint $table) {
$table->id();
$table->string('address_1');
$table->string('address_2')->nullable();
$table->string('city');
$table->string('state');
$table->integer('zip_code');
$table->timestamps();
});

How to select common date from three tables in laravel?

I have these three tables in my database and I want to select the common date from three tables to calculate the profit of every day , profit of every month, and profit of every year
this is my tables :
Schema::create('expenses', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->float('amount');
$table->string('month');
$table->string('year');
$table->string('date');
});
Schema::create('service_orders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('client_id')->unsigned();
$table->double('total_price', 8, 2)->nullable();
$table->string('month');
$table->string('year');
$table->date('date');
$table
->foreign('client_id')
->references('id')
->on('clients')
->onDelete('cascade');
});
Schema::create('orders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('client_id')->unsigned();
$table->double('total_price', 8, 2)->nullable();
$table->string('month');
$table->string('year');
$table->date('date');
$table
->foreign('client_id')
->references('id')
->on('clients')
->onDelete('cascade');
});
I think your problem can be solved by UNIONS
UNION used to combine the result of two or more tables
SELECT date FROM expenses
UNION ALL
SELECT date FROM service_orders
UNION ALL
SELECT date FROM orders
To use it in Laravel you can check documentation below:
https://laravel.com/docs/5.8/queries#unions

Yii2: How to do a simple join query?

I am learning how to do simple queries using the Yii2 framework. I use PostgreSQL.
I am trying to join two tables and get the data from both tables with a where condition.
The tables are called Admins and Persons.
The join use field called idadm.
The condition is idadm = 33. This works great but the result has data only from the Admins table and I need data from the other table.
Here is my example:
$query = \app\models\Admins::find()
->select('*')
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
I am following the Yii2 official guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
Update: Here I show the updated code that doesn't solve de problem:
You need to write all column name in select().
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
And also you need to define person table attributes in Admin model.
Second way is get records as array,so you dont need to define attributes in Admin model.
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->asArray()
->all();
Ensure that active record has required relations, e.g. something like follows:
class Admins extends \yii\db\ActiveRecord {
public function table() {
return "admins";
}
public function getPersons()
{
return $this->hasMany(Person::className(), ['idadm' => 'idadm']);
}
}
class Person extends \yii\db\ActiveRecord {
public function table() {
return "persons";
}
}
Then use joinWith to build query:
$query = Admins::find()
->joinWith('persons')
->limit(1);
$result = $query->createCommand()->getSql();
echo $result;
Here is produced query:
SELECT `admins`.* FROM `admins`
LEFT JOIN `person` ON `admins`.`idadm` = `person`.`idadm` LIMIT 1

eloquent refer to a column of a related a model

I have three tables:
categories
id, title
products
id, name
categories_products
id, category_id, product_id
I have also setup the according models and relationships (both have belongsToMany of the other)
Now I want to get all products belonging to a category
Category::where('title','Electronics')->first()->products()->limit(10)->get(['products.name']);
which works fine, but I also want to include the category title for each product as well:
Category::where('title','Electronics')->first()->products()->limit(10)->get(['products.name','category.title']);
However it returns: Column not found category.title
I thought that the relation would take care of it.
EDIT: Models -->
Category:
class Category extends Model
{
protected $fillable = array('title');
public function products()
{
return $this->belongsToMany('Product', 'categories_products', 'category_id', 'product_id');
}
}
class Product extends Model
{
protected $fillable = array('name');
public function categories()
{
return $this->belongsToMany('Category', 'categories_products', 'product_id', 'category_id');
}
}
The reason you're getting the error is because get() works just like select() and because you're running the category query and then running the product query after there is no categories table to reference for the select.
Look into Eager Loading. It will help with a lot of these kinds of issues. Your query can be written as:
Product::select('id', 'name')
->with(['categories' => function($query) {
return $query->select('id', 'title');
}])
->whereHas('categories', function($query) {
return $query->where('title', 'Electronics');
})
->limit(10)
->get();
Because we are lazy loading you NEED the id column on each model so Laravel knows where to attach the relationships after the queries are run.
The with() method above will eager load the categories relationship and the whereHas() method puts a relationship constraint on the current query.
UPDATE
Similar query from Category model:
$category = Category::where('title','Electronics')
->with(['products' => function($query) {
return $query->select('id', 'name')->limit(10);
}])
->first(['id', 'title']);
Then access the products with:
$category->products