I have one table named "files" which I would like to contain all my users files.
The files can be uploaded from different views and should be related to different tables: questions table, answers table, comments table, messages table etc... but still, all of them will be related also to users table, that way I will know who is the owner of the file.
Example: "user asking a question, and attaching a picture to help other users understand the question more easily."
The question values goes into 'questions_table', the file goes to 'files_table', and the user ID also goes to 'files_table'.
The question is! (sorry for the long introduction):
Should I use a pivot table? or just a double one-to-many relation from 1.'users_table' to 'files_table' & 2.from 'question_table' to 'files_table'?
I think this is the perfect use case for a Polymorphic Relationship.
Here is the structure of the tables:
users
- id
- name
questions
- id
- title
files
- id
- user_id
- filable_id
- filable_type
In the files table, you can see a filable_id field that is going to reference either a question id, answer id, comment id. And the filable_type that is going to tell the record which object is associated with this file.
class Question extends Model
{
/**
* Get all of the question's files.
*/
public function files()
{
return $this->morphMany('App\File', 'filable');
}
}
class File extends Model
{
/**
* Get all of the owning filable models.
*/
public function filable()
{
return $this->morphTo();
}
}
I highly encourage you to learn more about this type of relationship on the Laravel Documentation
Related
Hi I have a table in my database which contains a column with integer values which are actually the primary key of another tabel
Table 1: Details
column : subjects
value : 12,10,1,50,89,88
And another table is subject from where I should fetch the result. I want to know if I can fetch them using my model instead of repeating my code in controller everytime I need to fetch them.
I know you can define relations using
$this->belongsTo()
Or hasMany() depending on circumstances. I want to see If I can use this using scope on Model.
The final result I am looking for is json data returned from table Subject
I would recommend using a polymorphic relationship for this rather than the way you are comma delimiting in a single database table field.
You will have a new record for each association which may seem bad to you at first but it really is worthwhile, work with Eloquent so you don't need to add custom logic to explode those id's and associate manually.
students
id - integer
name - string
subjects
id - integer
name - string
subjectable_id - integer
subjectable_type - string
Then in your models something like this
class Subject extends Model
{
/**
* Get all of the owning subjectable models.
*/
public function subjectable()
{
return $this->morphTo();
}
}
class Student extends Model
{
/**
* Get all of the post's comments.
*/
public function subjects()
{
return $this->morphMany('App\Subject', 'subjectable');
}
}
You can then attach subjects to many different models the same way I demonstrated it attached to Students here.
Edit: If you would like to keep with your same interpretation of how this data is used, you could potentially do this in the Model but it's difficult to say without seeing the logic you are trying not to repeat.
I have a Question model which has a one to many relationship with an Answer model.
Now I want to add upvote/downvote funcionality to both of these models, do I need to create two tables like VotesQuestions and VotesAnswers or can I somehow manage with one? If so, how?
You can use a polymorphic relationship. This is built into Laravel. Documentation is here. The code shown here is for Laravel 4, but the functionality is the same for Laravel 5.
Create a votes table, and make sure it has at least two specific fields: votable_id and votable_type. In a database migration, you would use the statement $table->morphs('votable');, and it will create the two fields. You can have as many other fields as you like, but to make sure the relationship works, those two fields are required.
Next, setup the Vote model with the votable relationship. The name of this relationship should match the base name of the fields you created:
class Vote extends Eloquent {
public function votable() {
return $this->morphTo();
}
}
With this setup, you can now associate votes to any model you want. Go ahead and add the votes relationship to the Question and Answer models:
class Question extends Eloquent {
public function votes() {
return $this->morphMany('Vote', 'votable');
}
}
class Answer extends Eloquent {
public function votes() {
return $this->morphMany('Vote', 'votable');
}
}
You can now access the votes for any question/answer through the relationship:
$q = Question::first();
$qVotes = $q->votes; // Collection of votes for the question.
$a = Answer::first();
$aVotes = $a->votes; // Collection of votes for the answer.
You can also get the related question/answer model through the vote, if you ever need to:
$v = Vote::first();
$vRelated = $v->votable; // Will automatically be a Question or Answer object, depending on what the vote was for.
I would do an table for the question and when you want to up/downvote the question there should be a count column for both, otherwise you want to log it that an user can only vote for it once, so you need another table for user_id, question_id and type (up/down).
ofc you can handle it with one table, but that is really worth because you save many things that are not necessary.
you can create a table with an internal id, 1,2,3,4 and 1 is always the question or 0 and 2-xx (1-xxx) are always the answers. so you can handle it with one table
You could create a generic Votes model/table which has a field called "model" and "model_id" and then use reflection to get the correct object.
I am building a PHP application with Laravel where I have a Comment Model and many other models to which Comment can be attached to.
For example, a Profile can have many Comment but also a Post can have many Comment.
Should I split Comment in two models (ProfileComment and PostComment) or should I keep them together like this?
class Comment {
public function user()
{
return $this->hasOne('User');
}
public function profile()
{
return $this->belongsTo('Profile');
}
public function post()
{
return $this->belongsTo('Post');
}
}
If I keep them together, then on a database level how should I manage the comments table?
I was thinking about having the following columns:
integer: id - auto-incrementing id
integer: user_id - the user id
integer: foreign_id - profile/post id
varchar: type - which model? profile or post?
varchar: content - the actual comment
Is this the wrong approach?
if you want to use 1 table you will need to make Comment model morph.
if you don't want to use morph, make separated.
I think if you will use schema from your question, you will use morph.
I don't tkink where is need to make more tables, because all comments will have same structure (id, user_id (author), comment)
so in this case you will need just to make it morph (tagable).
I'm new to atk4, but I couldn't find some simple examples for using CRUD with many:many relationships between tables.
I read in some instructions that M:M is best implemented with intermediate table, which seems logical.
For example
Model ONE has:
$this->hasMany('Table1Table2','table1_id');
Model TWO has:
$this->hasMany('Table1Table2','table2_id');
And Intermediate Model (Table1Table2) has:
$this->hasOne('Table1');
$this->hasOne('Table2');
Which generates this table, which is OK:
create table table1table2 (
id int auto_increment not null primary key,
table1_id varchar(255),
table2_id varchar(255));
But how to implement CRUD? - how to implement listings, adding new, edit etc..
When on page I simply insert like this:
$this->add('CRUD')->setModel('Table1');
There is no relationship generated... It would be nice that user could select (on add and edit of table1) values from table2.
From reading and watching tutorials I have idea, but maybe is totally overwork, so I'm really asking what is the best way at ATK4 for this problem?
My idea:
add multiple dropdown to edit and add form and populate it with table2 values. If in edit, check in intermediate table for what is already checked.
extend basic CRUD, on formSubmitSuccess insert selected values from dropdown to intermediate table
do it differently for edit/add new..
later, do similar check for GRID&other stuff... can generate a lot of extra work..
So, I see its doable, but I'm sure M:M relationships are very common (it's basically two 1:M), so maybe is there much better solution?
No doubts you'll need intermediate table in relational DB design.
Question is, how you define Models. One solution is, as you already explained yourself, with 3 models. But you can also do something similar with just two models and using joins in your model definitions to join them directly to intermediate DB table.
Idea here is - Model is not the same as DB table in general. Model is something more than DB table. One model can join up like 10 tables and do something fancy with them :)
I guess there is no nice out-of-the-box solution for such interface (View) which will fit all needs. But in some cases, if you only need to set links (yes/no) between two tables, you can use form + grid + grid->addSelectable() or form + crud + crud->grid->addSelectable(). With such construct you can, for example, associate multiple user roles to users or Apps to Admins or favorite colors to people etc.
If you have more data fields in intermediate table than just linking id's, then you'll have to come up with some custom code. But I guess you can still take a peek on grid->addSelectable() method to grab some idea.
P.S. Sorry, this time I have no ready-to-use example :)
You need to create proxy table and create one to many relations between three tables
class Model_Admin extends Model_Table {
function init() {
parent::init();
$this->haveMany('AdminApp');
}
}
class Model_App extends Model_Table {
function init() {
parent::init();
$this->haveMany('AdminApp');
}
}
// this is proxy table
class Model_AdminApp extends Model_Table {
function init() {
parent::init();
$this->haveOne('Admin');
$this->haveOne('App');
}
}
actually there is no other way to create many-to-many relations
I can't find anywhere the information on how you have several intermediate tables with your Eloquent ORM models. The problem I'm facing is that I have a table for my users, permissions and roles. These are the 4 tables:
Permissions:
id
name
Permission_roles:
id
name
Permission_role_mappings:
id
permission_id
permission_role_id
Permission_role_user_mappings:
id
permission_role_id
user_id
(Well, I also have a users table but the layout of it doesn't matter since the foreign key is in permission_role_user_mapping.)
So the problem is that I want to be able to get the data from the permissions table when calling from the User model. I have some trouble grasping the workflow with Eloquent ORM altogether so if I'm missing something basic which is crucial then please point it out.
According to the documentation it seems that I don't need to create models for the intermediate tables. So how would I specify the relationship from the User class? Could I do something similar to this?
class User extends Eloquent {
public function permission_role()
{
return $this->has_many_and_belongs_to('Permission_Role', 'permission_role_user_mappings');
}
public function permission()
{
return $this->has_many_and_belongs_to('Permission_Role', 'permission_role_user_mappings')->has_many_and_belongs_to('Permission','permission_role_mappings');
}
}
This doesn't seem to be working, this is the error:
User::find(1)->first()->permission()->first();
...
Method [permission] is not defined on the Query class.
I also want to be able to get data by starting from Permission_Role and Permission. I'd prefer that the answer would help me specifying all the models required.
Eloquent relationships are accessed as an object property instead of a function.
User::find(1)->first()->permission;
You can wrap that above statement in the dd function to get a look at it.
This guide on Eloquent Relationships should be helpful
Edit for question in comments about selecting all permissions in the role:
$roles = array();
$permission_roles = User::find(1)->permission_roles()->get();
foreach ($permission_roles as $pr) {
if (! in_array($pr->permissions)) {
$roles[] = $pr->permissions;
}
}
This will get you what you want. However, this will end up doing a lot of queries. It's best to make use of Eager Loading here.