Laravel multiple relation in a same table - laravel

In my project, users can like & comment on feeds & forums. So, there is a contribution page where the user can see where he has provided his input (like or comment) sorted by created_at time.
There may be another feature in future like feed & forum where user can also provide like & comment.
In my contribution page, I want to list data like this -
You have commented on user_2's feed feed_title at created_at - comment
You have liked user_2's feed feed_title at created_at
You have commented on user_3's forum forum_title at created_at - comment
You have liked user_3's forum forum_title at created_at
But I am stuck in database design. So far I am trying this -
Schema::create('contributions', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('user_id');
$table->uuid('contribution_id'); // id of my like/comment
$table->string('contribution_type'); // feed_like/feed_comment/forum_like/forum_comment
$table->uuid('target_id'); // id of feed/forum where I provided comment or like
$table->timestamps();
});
But it will cause a query loop when I retrieve the data. So, is there any better approach to what I am trying to get?

You are probably looking for Polymorphic Relationships.
That enables you to simplify the relationship by providing an ID of the related model and a naming of the related model instead.
A sample migration would look like this, using the morph method as inspiration (since you're using UUID's):
Schema::create('contributions', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('user_id');
$table->uuid('contributable_id');
$table->string('contributable_type');
$table->timestamps();
});
This should enable you to do something like:
class Contribution extends Model {
public function contributable() {
return $this->morphTo();
}
}
class User extends Model
{
/**
* Get the user's contributions.
*/
public function contributions()
{
return $this->morphToMany(Contribution::class, 'contributable');
}
}
You should be able to retrieve the users contributions that way and defining the action based on the morphed instance type.

Related

Laravel - is there a library that lets me do a hasManyThrough relationship where my models PK is either value

Is there a library where I am able to use a pivot table that points to the same model twice (i.e User) where I can have the PK as either column 2 or 3 for something like a friending or associating system?
Code Example
migration
Schema::create('friends', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('requestor_id');
$table->unsignedBigInteger('requested_id');
$table->boolean('accepted')->default(0);
});
model
public function friends(){
return $this->[LibraryMethod](Friend::class, 'requestor_id', 'requested_id')->where('accepted', 1);
}
So then I could then either look at 'requestor_id' or 'requested_id' for my PK and get the PK of an associated User.
Ideally this should also work with eloquent functions.
I recognise that friends isn't the best example as you could have a handshake-like system, but for simple associations (i.e, between products) something like this would be pretty useful.

Best practice for storing a polymorphic belongsTo relation

We have a need for multiple models to share a documents relation. Due to that, I've used a polymorphic one to many as per the below schema.
Schema::create('client', function (Blueprint $table) {
$table->id();
$table->string('name');
});
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->foreignId('type_id')->constrained('document_types');
$table->string('path'),
$table->morphTo('documentable');
});
Documents Model
public function documentable()
{
return $this->morphTo();
}
Client Model
public function documents()
{
return $this->morphMany(Document::class, 'documentable');
}
Each document will need to relate to a specific type. For example, one document might relate to the customers signed agreement and another may relate to a completely different document - These types will need to be 'query-able'.
What's the best way to proceed with this? As far as I'm aware, my options are;
Create a document_types table and set a belongsTo relation on the documents table.
Create an enum column on the documents table.
A side note on this. This will be consumed via an API. There will be the following resources:
Route::apiResource('client', 'ClientController');
Route::apiResource('client.documents', 'ClientDocumentController');
With whichever solution is proposed above, how would you propose the API handles the document type when creating/updating a document?
If the use of a documents_type table, would the end-user have to hit a resource for the document type, to obtain the document type ID prior to creating/updating a document?
If the use of enums, would the API just expect a string for the given document type?
Many thanks in advance.

Creating a Custom ID (Primary Key) in Laravel

I have two tables(models) called Users and Books and need to create custom IDs (primary keys) values for my tables say, u25894r for users and b419k for books. I searched in the internet and stackoverflow a lot but I didn't understand WHERE I should define my custom ID generator function e.g., AppServiceProvider boot function or in Model constructor function. I'd be so appreciate it if you show the way with a little example.
EDIT:
I need to write functions that generate unique custom IDs for the tables.
You can set the custom id name in the Model class. Use
protected $primaryKey = "u25894r";
for user table and then make a migration.
You need to make a change in both the class/model definition (ie protected $primaryKey = "myUniqueName") and also in the migrations file:
public function up()
{
Schema::create('recruiters', function (Blueprint $table) {
// this is in the migration file:
$table->id('recruiter_id');
$table->timestamps();
});
}
you can set that in migration file
laravel documents is powerful for that you should read about migration from this link
https://laravel.com/docs/5.6/migrations

How to create a new many to many record with attach

Okey, i seen some posts about this but i don't understand the concept of attach at all, i have three tables:
Llistes(Lists):
$table->increments('id');
$table->string('nom_llista');
$table->integer('user_id')->unsigned();
});
Cancons(Songs):
$table->increments('id');
$table->string('titol');
$table->integer('genere_id')->unsigned();
$table->integer('artista_id')->unsigned();
$table->integer('album_id')->unsigned();
Pivot table: llistes_cancons (lists_songs):
$table->increments('id');
$table->integer('id_canco')->unsigned();
$table->integer('id_llista')->unsigned();
$table->timestamps();
I have two other Classes that i think that are correct, but i''m not sure:
In Canco.php (Song.php):
public function llistescancons_llistes()
{
return $this->belongsToMany('App\Llista');
}
In Llista.php (List.php):
public function llistescancons_cancons()
{
return $this->belongsToMany('App\Canco');
}
So, the question is how can I implement in my controller a function that let me add new record to the pivot table (many to many) and if it's possible another funtion to show the records, i'm newbie in Laravel and it's a bit hard for me.
There's no need to implement methods to add/remove records from the pivot table. Eloquent has attach/detach methods that can do that for you, but first you need to give Eloquent the column names of the pivot table since you are not using Eloquent's column name convention.
In Canco.php (Song.php):
public function llistescancons_llistes()
{
return $this->belongsToMany('App\Llista','llistes_cancons','id_canco','id_llista');
}
In Llista.php (List.php):
public function llistescancons_cancons()
{
return $this->belongsToMany('App\Canco','llistes_cancons','id_llista','id_canco');
}
Then if you want to attach a song to list, you can easily use the song id to do that
$list = App\Llista::find(1);
$list->llistescancons_cancons()->attach($songId);
or the other way around
$song = App\Canco::find(1);
$song->llistescancons_llistes()->attach($listId);

Laravel 5 - defining relationships

everything was working fine with a single Model, but now I am implementing more, I have noticed an issue.
I have several document Models which represent a different type of document. For now, let's say I have DocumentA and DocumentB.
Each Document allows file uploads, so I have created a FileUpload Model. A Document can have many FileUploads.
So, seems pretty straight forward at this point. My FileUpload table has a documentId field, which is a reference to the id field of the Document that is using it.
In DocumentA, I have something like so
public function uploadFile()
{
return $this->hasMany('App\UploadFile', 'documentId');
}
So DocumentA can have many UploadFiles, linked by the documentId.
DocumentB has the same function within its Model.
My problem lies with the UploadFiles model. Firstly, this model now has two belongTo events e.g.
public function documentA()
{
return $this->belongsTo('App\DocumentA', 'documentId');
}
public function documentB()
{
return $this->belongsTo('App\DocumentB', 'documentId');
}
This could be the problem, not sure if I can have multiple belongs to? My immediate problem however is to do with the migration of the doc_file table. At the moment I have this
Schema::table('doc_file', function (Blueprint $table) {
$table->integer('documentId')->unsigned()->default(0);
$table->foreign('documentId')->references('id')->on('document_a')->onDelete('cascade');
});
Schema::table('doc_file', function (Blueprint $table) {
$table->integer('documentId')->unsigned()->default(0);
$table->foreign('documentId')->references('id')->on('document_b')->onDelete('cascade');
});
So I am trying to provide foreign keys to my documents. When I try to migrate, it tells me that
Column already exists: 1060 Duplicate column name documentId
Am I handling my relationships correctly? Essentially, I have many documents, and each document can have many files.
Any assistance with my database relationships appreciated.
Many thanks
Looking at your problem at first glance, it seems that there is a little confusion for you regarding the concept model.
The Model Concept
A model is in fact a conceptualization of a real-world object as it is used to represent a real-world entity.
In other words, it represents a whole class of objects with similar properties. For instance a Car model would represent all cars, whether they are of type Lamborghini or Mercedez. The fact is that they all come under the Car classification.
Same concept goes in Eloquent, and with your use case; therefore a Document model is sufficient to represent both of your documents (DocumentA and DocumentB).
Eloquent Relationships
To refine what you've achieved so far, your models' relationships can be refactored as such:
Document Model
public function fileUploads(){
return $this->hasMany('App\FileUpload');
}
FileUpload Model
public function document(){
return $this->belongsTo('App\Document');
}
Based on the relationship "EACH document has MANY file uploads", and the inverse "EACH file upload BELONGS to exactly one document", as you can see, there is only one belongsTo() method in the FileUpload model to define the latter part of the relationship.
Similarly, the schema for the tables defining the above relationship are as follows:
// Schema for Document table
Schema::table('document', function (Blueprint $table) {
$table->increment('id');
$table->string('name', 100);
});
// Schema for FileUpload table
Schema::table('doc_file', function (Blueprint $table) { // file_uploads would have been a more friendly name in my opinion
$table->integer('documentId')->unsigned()->default(0); // note that `documentId` is interpreted as `documentid` in MySQL
$table->foreign('documentId')->references('id')->on('document')->onDelete('cascade');
});

Resources