One to many CakePHP3 (via matching table) - mysql

I was wondering, if it is possible to create a OneToMany Relationship in CakePHP3 via a matching Table in the DB.
This is what my DB looks like:
Showcase of DB Schema
This is my Table for ItemA:
$this->belongsTo('ItemB', [
'foreignKey' => 'item_a_id',
'targetForeignKey' => 'item_b_id',
'joinTable' => 'item_a_item_b'
]);
This is my Table for ItemB:
$this->belongsToMany('ItemA', [
'foreignKey' => 'item_b_id',
'targetForeignKey' => 'item_a_id',
'joinTable' => 'item_a_item_b'
]);
However, when I create a Control for ItemA in the template, it still gives me a Multiple Select.
echo $this->Form->control('item_b._ids', ['options' => $item_b, 'empty' => true]);
When I change this Form to Single Select, the selected Object will not be passed. I'm stuck to a Multiple Select :(
Is this the correct implementation of the DB Schema in Cake? Do I have to use the 'through' option? I'm confused...
Edit#1: It works if I configure ItemA with belongsToMany rather than belongsTo. But that would be a ManyToMany Relationship.

Traditionally, this isn't how you'd structure a one-to-many relationship. To enforce this at the DB level you would simply add tbl_item_a.id to the tbl_item_b table like:
tbl_item_b.item_a_id
However, by the sounds of it you know that already. And personally I don't see anything wrong with the setup as you have it, so long as it's documented (for others in the future).
By the sounds of it. The problem you're having isn't with the DB though, it's with the client-side rendering. For which you will want to use the select FormHelper function:
echo $this->Form->select(/*field name*/, /*[options]*/)

Related

CakePHP 3.x Saving new record but also new associated record

So I'm developing a system where a user can create a new account and upload a profile photo. I'm using two models: User for the account, Photo for the photos.
The users table has a foreign key on the field photoId that references to photos.id. The photos table has a userId field that has a foreign key referencing users.id. So there is a recursive relationship between User and Photo. Reason for this: when a Photo is created, we want to be able to see which User uploaded the Photo. A User has one Photo as his profile image.
So it's Photo hasOne User and User hasOne Photo. They are also configured this way in their Table classes.
Now, when a new User is saved, we want to also save a new Photo. Photo needs the id of the newly created User in order to save, but the User needs the id of the newly created Photo.
My question: what is the best method to save this? Should we allow User.photoId to be null, so that we can first save the User, then save the Photo, and then update the User with the correct Photo.id, or can CakePHP3 do some magic where it does all of this linking back and forth for you?
Data that comes from the form:
[
'username' => 'John Doe',
'mail' => 'john#example.org',
'password' => 'whatever',
'photo' => [
'name' => 'myImage.jpg',
'type' => 'image/jpeg',
'tmp_name' => '/private/var/tmp/phpN3dVmL',
'error' => (int) 0,
'size' => (int) 40171
]
]
Make Things Simple !
Why you need hasOne relations for both table ?
It looks like you are making simple thing so complected.Let's talk about that:
You have two tables users and photos that's fine.
At the moment each user has one photo that's fine as well.
Just imagine one day user might have multiple photo, then ?
Then still no worries, everything would be good because you have photos table already.
So what kind of relations do you actually need here ?
Just like mentioned above, user has one photo, That's why relation should be:
user hasOne photo and obviously photo belongsTo user.
And you need nothing more than that.
Just for example
Fields on Users table
id
name
email
address
has_photo // eg:if has_photo = 1 user has uploaded photo,such field is not compulsary
password...etc
Fields on Photos table
id
user_id // foreign key of users table
name
size
Relations
// UsersTable.php
$this->hasOne('Photos', [
'foreignKey' => 'user_id'
]);
// PhotosTable.php
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
Don't be confuse on that photos need user_id..users table never need photo_id.Because users table already associated with photos table. And if A hasOne B then B obviously belongs to A.That's the simple logic.
Don't hesitate to see the documentation ( Associations
) to get more information on relations..This is really important things for database management.
or can CakePHP3 do some magic where it does all of this linking back and forth for you?
Yes! As long as your associations are setup correctly Cake will take care of the rest. So the best method is to pass your data array into newEntity or patchEntity and make sure that all passed fields are accessible in you entity.
Saving hasOne relationships
Entities and mass assignment

Design best practice - model vs controller vs UI - CakePHP, MySQL

I have been struggling for a few days with this problem and finally seek the opinion of the experts and crowd at this website.
I have two tables - one is a template of workflow steps and the other is an instance of these workflow steps called events. The templates table contains information like step name, step type etc - very generic information. The event table contains a reference link back to the workflow step table and an additional column called notes - which stores data that the user logged as they logged a particular workflow step. Both Workflow Steps and Events are linked to a POST on the website
Workflow step templates can exist without events having yet occurred - that is the user may be still on Step 3 or Step 5 and not logged an event for Step 1, 2 , 4 - basically the order of steps is only suggestive but not binding. Workflow Steps have a sequence field that dictate the order in which they should appear on screen.
Events can also occur without a workflow step - in other words, a user can log a note outside the context of workflow steps. These are generic events and directly associated with the POST
I am able to successfully retrieve both of these values for a given POST - they are retrieved as two separate arrays. I am using CakePHP and MySQL
The UI needs to render a screen that shows all the workflow steps in order and corresponding events that have occured in correlation to these steps or outside of these steps. The ordering of the screen will be driven primarily by the sequence of workflow steps and secondarily by created_date for those events that are not associated with a particular workflow step
Problem statement -
1. Do I send two separate arrays (as noted in #4) to the UI and let the UI determine the complex logic of how to interweave the steps and events for display?
2. Do I process the interweaving of steps and events in the controller and then send to the UI a simple array that it can loop through and display?
3. I have tried moving this logic to the database but because of variations explained in #2 and #3 it becomes quite complicated
I am seeking advise on which would be a better option from a design practice as well as from a simplification point of view. I understand that I have given a limited picture here but am hoping that someone on this website may have run into a similar issue elsewhere.
Depending on how you are assigning events to users, I would make a hasOne relation in Event to Workflow. You would need another relationship for you users, hasOne or hasMany.
$hasOne = 'Workflow';
Obviously this would mean that your Event table would have a column called wordflow_id and would be associated with a single row in your workflow. In the controller I would call the Event with by the user.
$this->Event->findAllByUserId($user_id);
This should provide you with an array that might look something like this.
array(
[0] => array(
[Event] => array(
'id' => 1,
'name' => 'blah',
...
),
[User] => array(
'id' => 1,
'name' => 'Charles',
...
),
[Workflow] => array(
[0] => array(
'id' => 1,
'name' => 'more blagblagblag',
...
),
[1] => array(
'id' => 9,
'name' => 'sblagblsagblag',
...
),
[2] => array(
'id' => 42,
'name' => 'mordse d',
...
)
)
)
)
Call all your workflow templates
$this->Workflow->find('all');
Then I would user cake's built in SET:: functionality to print the workflow template in your view and use your Event call to fill in the data.
Please post more detail and your code, models, ect and I'm sure we can get you the exact query/logic you'll need to achieve this.
http://book.cakephp.org/2.0/en/core-utility-libraries/set.html
OK - I have solved this. I ended up moving the functionality to the Model.
I created two SQL queries - one that retrieves all the workflow steps along with any event information that maybe associated with each of them.
Then I created a second SQL that retrieves all those events that are stand-alone and not associated with any particular workflow step
I used UNION ALL to stack them on top of each other
I used a SORT on modified date and squence number so that all the steps and events appear in chronological order and sequence
I then passed this from the Model to the View (via the controller) and let the View iterate and display the elements. This approach actually simplified my View and Controller code immensely and even the Model code is quite simple since all it is a query statement with parameters.

ATK4 How OneToMany retion can be used with multiselect?

I have 2 MySQL DB tables called roles (representing user access role) and modules (representing allowed modules per role). Each role can have many modules and each module can have many roles.
I want to add a crud representing roles table with a multiselect field allowing to select all related modules. What is the best way to do that. Thanks in advance.
There can be multiple solutions for this.
First one - use User CRUD with expander with Roles (should work):
$crud = $this->add('CRUD');
$crud->setModel('User');
if (! $crud->isEditing()) {
// add subCRUD
$sub_crud = $crud->addRef('UserRole', array(
'extra_fields' => array('role'),
'view_options' => array('entity_name' => 'Role'),
'label' => 'Roles'
));
}
Second one - use Grid with Roles + grid->addSelectable($fields) (not tested, but just to give you idea):
$grid = $this->add('Grid');
$grid->setModel('UserRole');
$grid->addSelectable('selected');
Third one - use two lists with roles (available roles and associated roles) and some buttons to "move" role from one list to another.
Something like this: (can't find link to appropriate Codepad page now) :(
There definitely can be even more ways to do this.

Can I make two seemingly unrealated associations between two tables?

I am developing a resume app and have created a table called areas using the CakePHP Tree Behaviour. In this table, I have a bunch of countries and cities.
The original purpose of this table was to allow users to specify which areas they are interested in working in on their resume. This is pretty straightforward and I have created a HABTM relationship between users and areas to deal with this.
However, the next thing I need to do is allow users to specify where they are from. Of course, I could create a new table, identical to areas and call it something different, but this seems a bit wasteful and inefficient.
So, I am wondering if it is actually possible to have two totally different associations between the users table and the areas table?
In other words, the users table would have-and-belong-to-many areas (for the purpose of users specifying all of the areas they are interested in working in in the world) but it would also have-one area (for the purpose of users specifying which country they are from).
I can't begin to imagine how to go about properly setting up the models according to CakePHP convention -- or whether it is even a good idea to attempt this -- so I would appreciate any pointers anyone can give.
I am also happy to hear people's opinions even if the answer is not CakePHP-specific because this is more of a database question than anything else.
You can specify any association you want.
for example in your user Model:
/**
* #see Model::$hasAndBelongsToMany
*/
public $hasAndBelongsToMany = array(
'Area' => array(
'className' => 'Area',
'joinTable' => 'user_areas',
'foreignKey' => 'user_id',
'associationForeignKey' => 'area_id',
),
);
/**
* #see Model::$hasOne
*/
public $belongsTo = array(
'Farea' => array(
'className' => 'Area',
'foreignKey' => 'farea_id',
),
);

Kohana ORM store count in many-to-many relationship

I'm building a application using Kohana 3.2 and Kohana ORM.
The application has systems. Systems contain components. A system may contain multiple components, but also multiple components of the same type. E.g. System_A may have 10 Component_Y and 3 Component_Z
So instead of just having two belongs_to fields in my pivot table I also want to store the count.
If I just use a has-many-through I won't be able to access the count. Without ORM I'd just join the count onto the component in SQL, because the count is unique for the System + Component combination and so I can access the count for the component when I visit the object in the context of a certain system.
How best to go about this in Kohana ORM?
I solved it partially this way:
protected $_has_many = array(
'omvormer' => array(
'model' => 'omvormer',
'through' => 'systeemomvormer'
),
'systeemomvormer' => array(
'model' => 'systeemomvormer',
)
);
I've added the pivot tabel systeemomvormer separately to systeem.
I can now do this:
$so = ORM::factory("systeemomvormer");
$so->where('systeem_id', '=', $systeem_id);
$so->where('omvormer_id', '=', $omvormer_id);
$result = $so->find();
$result->aantal = $omvormer_count;
But it really still only is a partial solution, because I'm not able to update() the result. Kohana says that the result is not loaded. However that's outside the scope of this question and I'll open a new question for that.
This was also helpfull:
http://forum.kohanaframework.org/discussion/7247/kohana-3-orm-save-for-update-a-many-to-many/p1
To store more data in a junction table than just the two keys, you need to create a model for it.
So,
system _has_many system_component
component _has_many system_component
system_component _belongs_to component
system_component _belongs_to system
However, you may not need to store the count if you do it this way. Instead, you can do the following:
$system->components->count_all();
Then, to access them:
foreach($system->components->find_all() as $component)
echo $component->component->name;