This is my first time using a polymorphic relationship.
I am creating a LMS where a Assignment can be allocated to a individual user or a team so reading the Laravel docs it seems that the Polymorphic Relationship will be a good way to go about it.
I have created 4 tables.
Users:
| id | username | password | created_at | updated_at |
Teams: | id | friendly_name | slug |
Team User: | id | user_id | team_id |
Assignment Allocation:
| id | assignment_id | assignmentable_type | assignmentable_id | created_at | updated_at
So when the assignment_allocations has data in it looks like this...
| id | assignment_id | assignmentable_type | assignmentable_id |
| 1 | 1 | App\Models\User | 1 |
However I get this error:
SQL: select * from users where users.assignmentable_id = 1 and users.assignmentable_id is not null and users.assignmentable_type = App\Models\User
SO obviously I have done something wrong, however I cannot for the life of me figure out what I've done wrong.
This is my functions that relate to this:
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\User', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Model\Team', 'assignmentable');
}
Any help is greatly appreciated.
There are 2 potential answers depending on your data structure, which isn't clear from your question.
User/Team to Assignment is a one-to-many relationship (each Assignment has one User/Team, and each User/Team has many Assignments
Many-to-many relationship where each User/Team has many Assignments and each Assignment has many Users/Teams
One to many
In a one-to-many you wouldn't need an Assignment table and an AssignmentAllocation table, so I am assuming your AssignmentAllocation is your Assignment model. If not then you need to put the assignmentable_type and assignmentable_id columns on the assignments table instead, and use Assignment.php instead of AssignmentAllocation.php.
AssignmentAllocation.php
public function assignmentable()
{
return $this->morphTo();
}
User.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphMany('App\Models\AssignmentAllocation', 'assignmentable');
}
Your error is because Laravel is searching the users table for the match (you have morphMany('User'..), when it should be searching the AssignmentAllocation table. So just switch them out.
1) The morphMany acts like a hasMany, so you're saying:
Each User hasMany Assignments, and each Team hasMany Assignments. The first part of the morphMany says "search the AssignmentAllocation table", and the second part says "search for assignmentable_id and assignmentable_type being equal to this instance of this model".
2) morphTo acts like belongsTo so you're saying:
Each Assignment belongsTo an assignmentable.
Many to many
However if AssignmentAllocation is a many-to-many pivot table and each Assignment has many Users or Teams, and each User/Team has many Assignments, then you need a morphToMany/morphedByMany pair.
User.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Team.php
public function assignments()
{
return $this->morphToMany('App\Models\Assignment', 'assignmentable');
}
Assignment.php model NOTE: not the AssignmentAllocation model
public function users()
{
return $this->morphedByMany('App\Models\User', 'assignmentable');
}
public function teams()
{
return $this->morphedByMany('App\Models\Team', 'assignmentable');
}
You should rename the assignment_allocation table to assignmentables, or add the required third and fourth arguments to the morph functions. I prefer to keep the table names consistent.
Related
Hi Everyone just wondering if I am following best practices here.
I have step defintions like the following
public class StepDefinitions {
#DataTableType
public Author authorEntry(Map<String, String> entry) {
return new Author(
entry.get("firstName"),
entry.get("lastName"),
entry.get("famousBook"));
}
#Given("There are my favorite authors")
public void these_are_my_favourite_authors(List<Author> authors) {
// step implementation
}
}
and my feature file could then be something like
Feature: this is a feature
Scenario: this is a scenario
Given There are my favorite authors
|firstName| lastName |
| first | last |
Scenario: this is another scenario
Given There are my favorite authors
|firstName| lastName | famousBook |
| first | last | book |
So in the first step it will create an Author object but with famousBook == null.
Since I am creating objects used for REST requests and jackson will ignore null values is it okay to create objects like this?
You are using wrong data structure for your examples. You can refer to this resource giving the examples of different table types.
In your case you have a table
|firstName| lastName |
| first | last |
and try to parse it into Map<String, String> entry which would result in the following map:
[key1 = firstName, value1 = lastName]
[key2 = first, value2 = last]
If you need to treat it like header in top line then you need to parse it into List<Map<String, String>>
I'm developing a reddit-like site where votes are stored per-user (instead of per-post). Here's my relevant schema:
content
id | author_id | title | text
---|-----------|-------------|---
1 | 1 (adam) | First Post | This is a test post by adam
vote: All the votes ever voted by anyone on any post
id | voter_id | content_id | category_id
---|-------------|------------------|------------
1 | 1 (adam) | 1 ("First Post") | 1 (upvote)
2 | 2 (bob) | 1 ("First Post") | 1 (upvote)
vote_count: Current tally ("count") of total votes received by a post by all users
id | content_id | category_id | count
---|------------------|--------------|-------
1 | 1 ("First Post") | 1 (upvote) | 2
I've defined a voteCount relation in Objection.js model for the content table:
class Content extends Model {
static tableName = 'content';
static relationMappings = {
voteCount: {
relation: Model.HasManyRelation,
modelClass: VoteCount,
join: {
from: 'content.id',
to: 'vote_count.content_id'
}
}
}
}
But I recently (learned and) decided that I don't need to keep (and update) a separate vote_count table, when in fact I can just query the vote table and essentially get the same table as a result:
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
So now I wanna get rid of the vote_count table entirely.
But it seems that would break my voteCount relation since there won't be a VoteCount model (not shown here but it's the corresponding the model for the vote_count table) no more either. (Right?)
How do I keep voteCount relation while getting rid of vote_count table (and thus VoteCount model with it)?
Is there a way to somehow specify in the relation that instead of looking at a concrete table, it should look at the result of a query? Or is it possible to define a model class for the same?
My underlying database in PostgreSQL if that helps.
Thanks to #Belayer. Views were exactly the solution to this problem.
Objection.js supports using views (instead of table) in a Model class, so all I had to do was create a view based on the above query.
I'm also using Knex's migration strategy to create/version my database, and although it doesn't (yet) support creating views out of the box, I found you can just use raw queries:
module.exports.up = async function(knex) {
await knex.raw(`
CREATE OR REPLACE VIEW "vote_count" AS (
SELECT content_id
, category_id
, COUNT(*) AS count
FROM vote
GROUP
BY content_id
, category_id
)
`);
};
module.exports.down = async function(knex) {
await knex.raw('DROP VIEW "vote_count";');
};
The above migration step replaces my table vote_count for the equivalent view, and the Objection.js Model class for it (VoteCount) worked as usual without needing any change, and so did the relation voteCount on the Content class.
This question already has answers here:
count relation of relation in laravel
(3 answers)
Closed 3 years ago.
I am trying to count the amount of rows in a a relationship which is a via a relationship on the main table. I have 3 tables in total..
Surveys:
| id |
Questions:
| id | survey_id |
Responses:
| id | question_id |
I am trying to get the total count in the responses table from the surveys table. I have all of the relationships set up but I cannot seem to get the total count.
I have tried to do ->withCount('questions.responses') but this results in a 500 error. I know i can loop through but didn't know if there was a more efficient way to do it within Eloquent.
You can achieve it using a Has Many Through relationship.
In your model:
/* Survey.php */
public function responses()
{
return $this->hasManyThrough(Response::class, Question::class);
}
Then in your controller:
/* SurveyController.php */
public function index()
{
// Get your surveys with your desired attribute:
$surveys = Survey::withCount('responses')->get();
return view('my_cool_view')->withSurveys($surveys);
}
I'm trying to use Sequelize with legacy table and stuck how to define one-to-many connection of my database in Sequelize`s model.
I have the following tables:
pesons:
+----+---------+-------------+
| id | name | language_id |
+----+---------+-------------+
| 1 | Anatoly | 1 |
| 2 | Roman | 2 |
| 3 | Pavel | 1 |
+----+---------+-------------+
and
languages:
+----+---------+
| id | value |
+----+---------+
| 1 | English |
| 2 | Hebrew |
| 3 | Russian |
+----+---------+
Like you can see, every Person can know one Language but same Language can be known by different Persons.
var Person = sequelize.define('person', {
id : {
field: 'id',
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name : {
field: 'name',
type: Sequelize.TEXT
}
},{
tableName: 'persons',
timestamps: false
});
var Language = sequelize.define('language', {
id : {
field: 'id',
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
value : {
field: 'value',
type: Sequelize.TEXT
}
},{
tableName: 'languages',
timestamps: false
})
According to official manual: Difference between HasOne and BelongsTo
When information about association is present in source model we can
use belongsTo.
I need to define relations between Person and Language as:
Person.belongsTo(Language);
But it looks wrong to me and counter intuitive, so I'd like to get some clarification from somebody.
P.S. For the sake of clarity, I've never used any ORM before.
In usual case it would be every Person can know many Language but same Language can be known by different Persons
so there is many to many association so we do many to many association
http://docs.sequelizejs.com/en/latest/docs/associations/#nm
But for your use case kindly rephrase your scentence as
Languages has many persons (users), So here comes one to many association, there is no part of has one association.
belongs to and has many must be used in pairs, since they are counter parts
So first
Language.hasMany(Person)
The orm will add all the essential instance and class method to languages so that you can easily query, add and delete a person to a language.
Second part
Person.belongsTo(Language)
The orm will add all the essential instance and class method to Persons so that you can easily query, add and delete a Language to a person.
I think I'm kinda confused, I have a model whose some of the fields where to reference their detail/s (description/s) from another table.
eg.
**tblCustomers**
_______________________
Name | Address | Gender
-----------------------
A | A | M
B | B | M
C | C | F
**tblGender**
__________________
Code | Description
------------------
M | Male
F | Female
In my view here's how it look
Name A
Address A
Gender M <<< wherein what I wanted is something like
Name A
Address A
Gender M - Male
In my model, I am currently just doing something like these:
public function search($_id,$_curLevel)
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('Name',$this->name);
$criteria->compare('Address',$this->address);
$criteria->compare('Gender',$this->gender,true);
}
I know its pretty obvious, since the model is just looking into a single table, but I can't figure how to relate and use another table to be able to get the description from other reference tables.
The way I would do this, is to add a relation to my model.
I am assuming you have a model setup for the gender table, and it is called Gender. I would also rename your 'gender' column in your customer table to genderID or similar. I have renamed it for my example.
Add this code to your Customer Model:
public function relations()
{
return array(
'gender' => array(self::BELONGS_TO, 'Gender', 'genderID'),
);
}
Then once you have created this relationship, it is very easy to extract the information in your view.
You already have the model, so now the view looks like this:
echo $model->name;
echo $model->address;
echo $model->gender->description;
Note that 'gender' in the line above is referring to the 'gender' relation we created, not the column that you had named 'gender' in the description table (I renamed that to genderID).
If you just wanna the gender description then you can user the relation and display $model->gender->description; as descriped in the above answer
If you wanna a custom text as you mentioned "M - Male" then you have to add a public property and fill it in the afterFind() method
class Customer extends CActiveRecord {
public $_gender;
public function afterFind() {
$this->_gender = $this->gender . ' - ' . $this->gender->description;
return parent::afterFind();
}
}
note: that gender relation must be exist