In the Getting Started section we created and scaffolded tables for Members, Courts and Bookings in the tennis club. Subsequently we modified the booking screen to allow the user to select the foreign keys, courtid and memberid from dropdowns on the booking/new screen. This certainly improved user experience and made it easier then having to know off the relevant id number but it is still a way of being ideal. For one thing it is not very secure. It's very easy to make bookings in another member's name which leaves the system open to abuse and hacking.
Ideally, when making a booking, the member id should somehow be retrieved from the user's login details. This will make it simpler and more secure when making a booking.
In order to achieve this, we need to introduce a relationship between the Member and User tables/classes. There are a number of approaches to introducing this relationship. In purely correct terms it is likely that a Member "is a type of" User. This introduces what in relational databases is terminology a Specialization-Generalization relationship and in Object Oriented Programming is referred to as Inheritance. A UML model to represent this might look something like this:
The reality of specialisation/generalisation is that it does not map very naturally to a relational database approach. That said, there are a range of ways to implement a relationship like this. In a pure "type-of" relationship the tables would share a primary key. That is the primary key userid would also be the primary key in the Member table. In the member table it would be both the primary key of that table and a foreign key of the user table. In order to make this work we would need it to auto_increment in the User table but not the member table.
Having introduced the User table in the way we did also introduces a problem in relation to normalization and functional dependency within our database. We now have firstname and surname stored both in the User and in the Member table. This breaks cardinal rules in relation to databases.
Despite these issues, we really need some way to pick up the member's id from the logged in session information. One thing we could do would be to change our database around so that it's the User that makes a booking and has a relationship with the booking table. This would probably work but given that we have an existing system in place and a whole array of functionality and user interfaces that rely on the Member model would be too dramatic at this stage.
A more straightforward approach will be to introduce a one to one relationship between the member and the user table by adding a userid foreign key field to the member table. It's not a perfect model for the way this relationship truly "is" but it can be made to work well in this situation.
Modify the member table in the database using the following sql script.
alter table member add column userid int; alter table member add constraint FK_member_user foreign key(userid) references user(id);
Now regenerate the Member model and the User model. To do this delete the Member.php and the User.php from app/models (take care to back up these files before deleting them in case you have introduced any additional functions/changes to these classes e.g. the getAge() function or a __toString() magic method). Then regenerate the models using the following commands in the CLI.
phalcon scaffold member --get-set --ns-models=tennisClub phalcon scaffold user --get-set --ns-models=tennisClub
* You can use the "phalcon model --force" command to re-create the model class but this doesn't always produce consistent results, particularly when using namespaces.
Now have a look at the initalize method of the member model. This should have picked up on the foreign key relationship and created a hasMany relationship between the models. We need to modify this to be a hasOne relationship.
Edit the file app/models/Member.php. The scaffolder will have generated the attribute and necessary getters and setters for the userid column in the database. In addition, it will generate a relationship something like the one below.
We need to change this relationship to be a hasOne relationship. Change the line of code highlighted above to the following.
$this->hasOne('userid', 'tennisClub\User', 'id', ['alias' => 'User']);
Now edit the file app/models/User.php. The scaffolder has added a hasMany relationship with the Member model. This also needs to be changed to a hasOne relationship.
Replace the highlighted line above with the following line
$this->hasOne('id', 'tennisClub\Member', 'userid', ['alias' => 'Member']);
We now have a hasOne relationship on both models which means whenever we want to get the memberID we can walk the object tree by using
$thisMemberID = $this->session->get('user')->getMember()->getID();