Propel ORM - One-to-many relationship with parent name - propel

Is it possible to create a parent name field into one-to-many relationship in Propel ORM.
This type of relationship uses in CRM systems.
Just imagine that we have a Task List. So, we created a Task #1 and related it to a Project.
Task #2 is related to Account (e.g. create a contract).
Task #3 is related to Bug Tracker (e.g. fix a bug).
So, we have the following relationships:
task_name | parent_name | parent_id
--------------------------------------------------
Start a project | Project | <project_id>
Create a contract | Account | <account_id>
Fix a bug | Bug Tracker | <bug_id>
Is it possible to implement in Propel. If no, could you recommend me another ORM with this feature.
The main purpose is to get a list of records with all relationship values.
For my example, it should look like (in JSON):
{
"Task_0":{"Id":1,"Name":"Start a project","ParentId":1,"ParentName":"Project","Project":{"Id":1,"Name":"Project-1","Tasks":{"Task_0":"*RECURSION*"}}},
"Task_1":{"Id":1,"Name":"Create a contract","ParentId":1,"ParentName":"Account","Account":{"Id":1,"Name":"Account-1","Tasks":{"Task_0":"*RECURSION*"}}},
"Task_2":{"Id":1,"Name":"Fix a bug","ParentId":1,"ParentName":"Bug","Bug":{"Id":1,"Name":"Bug-1","Tasks":{"Task_0":"*RECURSION*"}}}
}
Does anyone help me?

The output you have shown looks as if the toArray function has been used on the Propel objects and then the json_encode function. This should work if you define foreign keys mutually in Propel's schema.xml.
Since project tasks, account tasks and bug tracker tasks all have something in common, they all are tasks :), I would organize them as sub classes of a more general task entity.
You will end up with a collection of tables like this:
Table "task"
id | name
------------------------
1 | Start a project
2 | Create a contract
3 | Fix a bug
4 | Start another project
5 | Fix another bug
---------------------------------------
Table "bugtrack_task"
id | id_task
---------------
1 | 3
2 | 5
---------------------------------------
Table "project_task"
id | id_task
---------------
1 | 1
2 | 4
---------------------------------------
Table "account_task"
id | id_task
---------------
1 | 2
In the end, you would define a view in the schema.xml. This could look something like this:
<table name="view_task" phpName="ViewTask" skipSql="true" readOnly="true" description="All my tasks together for display">...</table>
Note that the skipSql attribute has been set to true. This will skip this view table when generating the SQL code. Propel will generate the classes for you but won't touch your database. You can now manually define the view yourself putting into it whatever you desire.
Of course you'd have to put some effort into creating this view but it pays off as you will be able to use the Propel classes like so for instance:
$tasks = ViewTask::create()->find();
$result = array();
foreach($tasks as $task) {
$result[] = $task->toArray();
}
return json_encode($result);
This isn't a complete answer but I hope you see the idea! Good luck :-)

Related

Custom fields design for multiclient application

I have a question releated to application design. I have a ecommerce application which is used by 9 clients. Every client has the same copy of application with different frontend template. App is designed under laravel and it's updating on every customer server to keep up-to-date. So every app have the same backend("engine"), same database design etc.
Problem is that two of the clients wants a custom fields to for CRUD pages. With current update mechanism every client will get those fields which is not what i want.
I've been thinking about adding separate table to database to keep there a configuration of all fields - like a map of table columns. So when app is used controller will call configuration table to get list of fields and foreach them in view.
+---+---------------+-------------------+------------+
|id |controller | field_name |field_type |
+---+---------------+-------------------+------------+
| 1 | products | price_retail | integer |
| 2 | manufacturers | name | varchar |
| 3 | manufacturers | logo | varchar |
| 4 | manufacturers | custom_for_client | integer |
+---+---------------+-------------------+------------+
Is it a valid - good idea?
Without knowing exactly what you plan to do with these "fields", I can only make suggestions.
Have you considered the use of a json column to store data for fields that aren't applicable to all users?
Database Migration
...
$table->json('meta')->nullable();
...
Model
...
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
...
'meta' => 'array',
...
];
...
You can then access the "fields" within this json object like so:
$model->meta['column'];
or iterate over then like so:
collect($model->meta)->each(function ($column) {});
https://laravel.com/docs/7.x/eloquent-mutators#array-and-json-casting

Joining an additional table with belongsToMany()?

This question is best illustrated by an example:
users
id
name
roles
id
name
role_user
user_id
role_id
rank_id
group_id
...
ranks
id
name
groups
id
name
I can easily eager load a users table by specifying the following relationship in my User.php model:
public function roles() {
return $this->belongsToMany('Role');
}
Which will output the table below when calling User::with('roles'):
User | Role
-------------
Jon | Admin
Jan | Mod
However I have no idea how to extend this to include:
User | Role | Rank | Group
-----------------------------
Jon | Admin | Boss | Blue
Jan | Mod | Minion | Red
What I've tried doing User::with('roles', 'ranks', 'groups') but that is certainly wrong since I'm telling Laravel there are rank_user and group_user intermediate tables too but there aren't. What is the correct way?
PS: I know it's better to separate the ranks and groups into their own relationship/pivot tables, this is simply an example.
EDIT: Closest example I can find for this: https://github.com/laravel/framework/issues/2619#issuecomment-38015154
You can just treat your model's relations methods as ordinary queries and build upon them:
public function roles() {
return $this->belongsToMany('Role')
->join('role_user', 'role_user.role_id', '=', 'roles.id')
->join('ranks', 'ranks.id', '=', 'role_user.rank_id')
->join('groups', 'groups.id', '=', 'role_user.group_id');
}
Relations queries like the above are not so intuitive to understand when they get too complex, so it may be better to rethink database design, but in theory it's possible to manipulate them.

Problems adding roles to a user model in Laravel 4

I'm having some trouble using Eloquent models and not sure if I am missing something. I have a User model and a Role model with a many to many relationship set up in Laravel 4...
// Find a user with their roles (none at the moment)
$user = User::with('roles')->find(2);
var_dump($user->toJson());
Notice the empty roles array, which is expected
{
"id":"2","username":"Test","email":"test#example.com","remember_token":"",
"roles":[]
}
Next, I attach a role to the user and show the user again...
$user->roles()->attach($role);
var_dump($user->toJson());
Now we have attached a role to the user, but the roles array is still empty
{
"id":"2","username":"Test","email":"test#example.com","remember_token":"",
"roles":[]
}
If I try and fetch the user again, the roles show up fine, so the DB is being updated correctly...
$user = User::with('roles')->find(2);
var_dump($user->toJson());
{
"id":"2","username":"Test","email":"test#example.com","remember_token":"",
"roles":[{"id":"1","name":"Admin","pivot":{"user_id":"2","role_id":"1"}}]
}
My Question: How come after I attach a role to my user model, it does not show up in the model until I reload it from the DB. I would have expected it to be reflected in the in-memory version of the model as well.
Since user has many role, you need a query to get all roles.
For example, if you attach new roles to a user, and that user has already another roles, then there will be a confusion.
You may try to set an attribute to your model after calling attach method, to do this please read the last thing on the Laravel Eloquent doc
protected $appends = array('local_roles');
Edit
Even if you pass a model instead of id (with save method), it will not be added to the collection, and I find it more logical. Because as I already said, it may be a difference between the database and the model. Let me explain more clearly:
Role table
-------------
| id| name |
-------------
| 1 | admin |
-------------
| 2 | root |
-------------
role_user table
-----------------
|role_id|user_id|
-----------------
| 1 | 1 |
-----------------
| 2 | 1 |
-----------------
user table
-----------
|id| name |
-----------
| 1| razor|
-----------
Now, suppose the attach(or save) method can add the model to the collection.If you do
$user = User::find(2); // without "with('roles')"
//Then you add a new role
$role = new Role;
$role->name = 'another_role';
$user->roles()->save($role);
Then, you will have in your model
"roles":[{"id":"3","name":"another_role","pivot":{"user_id":"1","role_id":"3"}}]
witch contradicts the results from the database. This problem does not appear in belongs_to and has_one relations, because there is only one model associated and even if you don't use with method, you get the same result. But in many relations(belongstomany,hasmany,hasmanythrough), the result will be depend on the use of the with method.

Laravel 4: one to many by on multiple columns?

I'm making a table that essentially maps rows in a table to rows in another table where the structures are as follows:
|--- Words --| |- Synonyms -|
|------------| |------------|
| id | | id |
| en | | word_id |
| ko | | synonym_id |
| created_at | | created_at |
| updated_at | | updated_at |
|------------| |------------|
Now then, I know I can essentially have the words model have many Synonyms through a function like:
public function synonyms()
{
return $this->hasMany('Synonym');
}
No problem, but this method always gets it by the the word_id, and I would like to get it from word_id OR synonym_id that way I don't have to make multiple entries in the DB.
Is there anyway I can do this?
Check laravel docs Eloquent relationships. It would only get word_id because that's the only foreign key I believe.
Also why do you have synonym_id in your Synonyms table?
I believe you are looking for polymorphic relationship.
http://laravel.com/docs/eloquent#polymorphic-relations
I think your best bet is to create a many-to-many relationship with words on itself using the synonyms table as your pivot table.
Add this to your Word model.
public function synonyms()
{
return $this->belongsToMany('Word', 'synonyms', 'user_id', 'synonym_id');
}
Using it:
$word = Word::where('en', '=', 'someword')->first();
foreach($word->synonyms as $synonym) {
// This method would probably return the same word as a synonym of itself so we can skip that iteration.
if($synonym->en == $word->en) {
continue;
}
// Echo the synonym.
echo $synonym->en;
}
I'm a bit confused on you wanting to be able to find synonyms by the word_id or synonym_id but I think if you are using the many-to-many, it won't matter because if you know the synonym, it's still technically just a word, and you'd do the exact same thing.

one to many save, codeigniter DataMapper

Working on a site where a user can add videos. Each video can be in many sections. It can also have many questions. Video has a one-to-many relationships with both the question and section classes.
I'm getting the section and video classes like this:
$s = new Section();
$s->where('section', $this->post->section)->get();
then saving like this:
$v->save($u, $s, $q);
where $v is a video object, $u is a user object and $q is a question object.
I want to allow the user to POST multiple questions and sections. How do I save those relationships. Should $s and $q be arrays of objects?
I am not 100% sure that I understand what you mean but yes yu can save multiple relations at the same time.
Like this:
$s = new Section();
$s->where_in('section', $array_with_sections_ids)->get();
$v->save(array($u, $s->all, $q));
You use different tables in your database. Like Video, Question and VideoQuestion where you store the video ID with the different Question id's
Then VideoExample would look like:
| video_id | question_id |
--------------------------
| 1 | 1 |
| 1 | 5 |
...
for inserting this data, first insert the video and get its ID with
$videoId = $this->db->insert_id()
Do the same for your question and you've got your ID's ;)
The same for the other tables and you're done. Good Luck!

Resources