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

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();
});

Related

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

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

Laravel 5.3 Eloquent Relationship 1-1 Query

Hi guys how could I query a data from my database with one-to-one relationship on eloquent model?
I want to query the menus with specific category id.
For example I only want to query the meals with "Breakfast Category".
Menu Model:
public function menucat()
{
return $this->hasOne('App\MenuCat', 'menu_cat_id', 'id');
}
Menu_Cat Model
public function menus()
{
return $this->belongsTo('App\Menu', 'menu_cat_id', 'menu_cat_id');
}
Database Table:
menus Table
id | menu_cat_id | menuName
menu_cat Table
menu_cat_id | menuCatName
I find it easy using the Query builder but I would like to use the eloquent to query out the data I need.
Thanks in Advance!
in the documentation you have that
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
you need to inverse menu_cat_id and id like that
return $this->hasOne('App\MenuCat', 'id', 'menu_cat_id');

Laravel 5 hasManyThrough Pivot Table

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).

Sails.js/Waterline cascading delete for a many-to-many association

As shown in that stackoverflow answer, having no support for cascading (cascading deletes in particular) in Waterline there is a workaround for one-to-many associations by using the afterDestroy (or afterUpdate for soft-delete) lifecycle callback and deleting the associated records with a second query. This is possible via ManyModel.destroy({ oneModel: _.pluck(destroyedOneModels, "id") }) from within afterDestroy.
How do we do that for a many-to-many relationship (having in mind that a junction table is used internally and we have to delete records from it)?
I did some tests using the Pet / User example from the documentation with sails 0.11.
Writting this lifecycle callback in the Pet model deletes all the users associated to a pet before deleting it.
// models/Pet.js
module.exports = {
attributes: {
name:'string',
color:'string',
owners: {
collection: 'user',
via: 'pets'
}
},
beforeDestroy: function(criteria, cb) {
// Destroy any user associated to a deleted pet
Pet.find(criteria).populate('owners').exec(function (err, pets){
if (err) return cb(err);
pets.forEach(function(recordToDestroy) {
User.destroy({id: _.pluck(recordToDestroy.owners, 'id')}).exec(function(err) {
console.log('The users associated to the pet ' + recordToDestroy.name + ' have been deleted');
});
});
cb();
})
}
};
I couldn't do it in the afterDestroy lifecycle callback because the many-to-many properties of the deleted records are missing there.
Waterline is deleting the records of the junction table automatically.
The problem with this feature is that it probably would delete too much things if some pets share some owners. Following the example of the documentation, if you delete the pet Rainbow Dash, you will delete the users Mike, Cody and Gabe, and the pets Pinkie Pie and Applejack would be orphans.
If you define a many-to-many relation like this one but you know that the pets cannot have any owner in common, then it works fine. Otherwise, you should add a test to check that you will not make another pet an orphan.