Laravel inserting into a three-way pivot table - mysql

Summary
I am building a music discovery service. My question is: How do I insert data into the three-way pivot table Tag_Track_User ?
Schema
I have this schema seen here at LaravelSD
It comprises of six main tables (and a few others):
Artists, Albums, Tracks, Tags, Users and Tag_Track_User
The Artists->Albums->Tracks relationship is straightforward and as you'd expect.
Tags, Tracks and Users all relate to one-another as no two can exist without the third.
Relationships
Artists hasMany() Albums
Albums hasMany() Tracks and belongsTo() an Artist
Tracks belongsTo() Albums
Tracks belongsToMany() Tags and belongsToMany() an Users
Tags belongsToMany() Tracks and belongsToMany() an Users
Users belongsToMany() Tags and belongsToMany() an Tracks
Models
User model
public function tags()
{
return $this->belongsToMany('Tag', 'tag_track_user', 'user_mdbid', 'tag_mdbid')->withPivot('track_mdbid');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tracks()
{
return $this->belongsToMany('Track', 'tag_track_user', 'user_mdbid', 'track_mdbid')->withPivot('tag_mdbid');
}
The Tag and Track model contain the same respective relationships.
Question
So my question is:
How do I insert data into the Tag_Track_User table? The tag_track_user table is a 3-way pivot table cointaining information about tracks that users have tagged.
You have to be logged in to tag a track (which means I have access to the user’s ID). The tracks ID is accessed as I am displaying it on the page where the form is contained. The tag on the other hand; if it already exists in the tags table, I want to get it’s ID and re-use that (as they are unique), if not, I want to create it, assign it an ID and insert that into the tag_track_user_table.
I need to check whether the Tag exists
If it does, get it's ID
Insert data into the Tag_Track_User table
Thank you
Any help I receive on this, is greatly appreciated.

Well:
$tag = Tag::firstOrCreate(array('text' => $tag_text));
TagTrackUser::create(array(
"tag_mdbid" => $tag->mdbid,
"track_mdbid" => $track->mdbid,
"user_mdbid" => Auth::user()->mdbid
));
Something like that? firstOrCreate does what the name says it does, the rest is pretty straightforward Eloquent.

Since seems that there is not an appropriate pattern in Laravel, the cleaner and easier way is to implement any three-pivot-relationships via a model dedicated to the pivot table:
class Track
public function trackTags()
{
return $this->hasMany('TagTrack');
}
...
class Tag
public function tagTracks()
{
return $this->hasMany('TagTrack');
}
...
class TagTrack
public function track()
{
return $this->belongsTo('Track');
}
public function tag()
{
return $this->belongsTo('Tag');
}
public function anotherRelationship(){
...
}
You can do:
$track->trackTags->myCustomPivotDataAndRelationship()
and in TagTrack you have freedom to add as many relationship and field I want
Note than you can still keep the many to many to use when you don't need to access pivot relationships

Related

How I can get data from table laravel model

I got two tables (Models),
tags with id and name and
post_tag with post_id and tag_id.
How I can get from table tags name but with table post_tag post_id.
Look, you haven't given any code sample. But from your question, I guess that you've two model named, Tag & Post
So, your post_tag became the pivot table right. And it is a many-to-many relation.
In your Tag model build relation like,
public function postTag()
{
return $this->belongsToMany(Post::class, 'post_tag', 'tag_id', 'post_id');
}
In same way, in your Post model add a relation like
public function tags()
{
return $this->belongsToMany(Tag::class, 'post_tag', 'post_id', 'tag_id');
}
Now, your pivot relation is ready. At the time off attaching tags with Post, use $post->tags()->attach(Tag::find($tag)); // $post = new Post(); $tag is tag_id
To retrieve all post with associated tags, call
Post::with('tags')->get();
Similarly, to get tags associated with post
Tag::with('postTag')->get();
Head to laravel official website for many-to-many relations documentation
laravel One To Many Eloquent

Laravel eloquent: Save data with relationship

I created an invoice form which has a section where users can dynamically add (via jquery row add) items to be invoiced.
I need to save these data into two tables: Invoice and Invoiceitems. The two tables have one to many via MySQL relationship and Laravel models have hasMany and belongsTo relation assigned.
My question is how to save the data into Invoiceitems table.
In your App\Invoice, define your relationship with App\InvoiceItem as follows:
public function items()
{
return $this->hasMany(InvoiceItem::class);
}
In your App\InvoiceItem model, define your relationship with App\Invoice as follows:
public function invoice()
{
return $this->belongsTo(InvoiceItem::class);
}
To create an Invoice with many InvoiceItem, you would then do something like this:
$invoice->items()->saveMany([
new App\InvoiceItem(['title' => 'iPhone X']),
]);
Read more about Eloquent Relationships.

Laravel 5 Read Only View Model with polymorphic Relationship

Sometimes we use MySql Views to organize related tables to make it easier to search and sort. For example if you have Posts with a Status, and a Source.
Post
subject
body
source_id
status_id
Status
id
label
other_field
Source
id
label
other_field
View
create view read_only_posts as
SELECT statuses.label as status, sources.label as source, posts.*
from posts
left join statuses on statuses.id = posts.status_id
left join sources on sources.id = posts.source_id
Then we have the Post model and an extra model:
// Post.php
class Post extends Model
{
//
}
// ReadOnlyPost.php
class ReadOnlyPost extends Post
{
protected $table = 'read_only_posts';
}
This is nice because now you can directly sort or filter on Status or Source as a string not the id's. You can also include the 'other_field'.
But we have a problem that I need help with. If you have a polymorphic many-to-many relationship on Posts, I can't get it to work on the read only version. For example if you have polymorphic tags:
// Post.php Model
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
The problem is when you filter a post (using the read only model) with a specific tag you get sql like this:
select count(*) as aggregate from read_only_posts where exists (select * from tags inner join taggables on tags.id = taggables.taggable_id where read_only_posts.id = taggables.taggable_type and taggables.taggable_type = 'read_only_posts' and label = 'test')
As you can see the problem is the taggables.taggable_type = 'read_only_posts'.
I can't find a way to override the morph type for a model. (I am on laravel 5.4 and the MorphClass isn't there anymore). The morph map is an associative array so you can't do this:
// AppServiceProvider
public function boot()
{
Relation::morphMap([
'posts' => Post::class,
'posts' => ReadOnlyPost::class, <--- Can't do this
My stupid fix is when I attach a tag to a post I also attach it to ready_only_posts, which is kind of a mess.
Anyone else uses Views for read only models? Anyone have a better way to overriding the many to many polymorphic type for a specific model?
Looking at the code, I believe this might work.
class ReadOnlyPost extends Posts
{
public function getMorphClass() {
return 'posts';
}
}
In theory you should need to have the Posts model/table listed in the morph map, since the system will auto generate the type of "posts" for it based on naming.

Relationships between tables in laravel using backpack package

I am using backpack CRUD package to create my website project in laravel 5.2
I want to establish a relationship between two tables. First table is called customer and second table is called transaction. Each customer has many transaction(1:N relationship).
Customer table record:
ID Name
123456 xyz
Transaction table record:
ID CustomerID
101010 123456
I know that I have to specify the relation in the customer model. But, how can I display the result of the relationship in CRUD ?
You should have relationships on both the Transaction and the Customer models, so you can do $customer->transactions and $transaction->customer:
class Customer extends Model
{
/**
* Get the comments for the blog post.
*/
public function transactions()
{
return $this->hasMany('App\Transactions', 'CustomerID', 'ID');
}
}
and
class Transaction extends Model
{
/**
* Get the comments for the blog post.
*/
public function customer()
{
return $this->belongsTo('App\Customer', 'CustomerID', 'ID');
}
}
Spend some time in the Eloquent Relationships Documentation. It's really important to understand them if you want to be a Laravel developer.
In order to display the relationship in the CRUD, you can then use Backpack's select column type to display it in the table view and select or select2 field types to display it in the add/edit views. Read the CRUD Example Entity to better understand how that works.
First of all when you are creating migrations for both tables, table which contain Foreign Key (FK) must have field like this:
public function up(){
$table->increments('id');
$table->integer('customerID')->unsigned();
}
After that you are need to call next command into console
php artisan migrate
Next is going next commands:
php arisan backpack:crud customers
php arisan backpack:crud transactions
After that you need to define functions in models which returns values from other tables. Customer models need to have next function
public function transactions(){
return $this->hasMany('Transaction');
}
Transaction model must have next function
public function customer() {
return $this->belongsTo('Customer');
}
Next you must add CRUD field in Customer controller to display
transactions in select box.
$this->crud->addField([
'label' => 'Transactions', // Label for HTML form field
'type' => 'select2', // HTML element which displaying transactions
'name' => 'customerID', // Table column which is FK for Customer table
'entity'=> 'customer', // Function (method) in Customer model which return transactions
'attribute' => 'ID', // Column which user see in select box
'model' => 'Transaction' // Model which contain FK
]);
Hope this helps :)
After you built onetomany relationship with transaction, you can get the results.
$customer=Customer::where(['id'=>'123456'])->with('transaction')
->first();
print_r($customer->Name); // gives the customer name
foreach($customer->transaction as $cid)
{
print_r($cid->CustomerID); // gives the customer id
}
Laravel Relationships Documentation is always helpful. Go through it.

How to define a relation between three tables and a junction table

Ok, I'm new to yii2 (and web development, as a matter of fact) so take it easy on me.
If I had a many-to-many relation between two tables, say, Environment and Category, and a junction table relEnvCat, I would have something like this:
public function getEnvironments()
{
return $this->hasMany(ENVIRONMENT::className(), ['PKENVIRONMENT' => 'FKENVIRONMENT'])
->viaTable('RELENVCAT', ['FKCATEGORY' => 'PKCATEGORY']);
}
But what should I do if I had three tables and a junction table. Exemple with their primary keys: CI(pkCi), Environment(pkEnvironment)and Context(pkContext).
Then I have a junction table, named relCiEnvCont with these foreign keys (fkCI, fkEnvironment, fkContext).
But, I don't know how to define this triple relation on yii... Can anybody help me?
As far as I know, which isn't much, all I can do is this:
class CI extends \yii\db\ActiveRecord
{
....
public function getRELCIENVCONTs()
{
return $this->hasMany(RELCIENVCONT::className(), ['FKCI' => 'PKCI']);
}
...
}
And that is not good at all... I'm thinking maybe these relations are far too complex for Active Records and I should ditch it and use query builder instead?