CakePHP, working with HABTM tables - php

I'm working with CakePHP 2.x and I was wondering if there is any built-in functionality for working with HABTM join tables. I don't really see anything in the documentation for directly changing the attributes of a HABTM relationship. This is what I mean:
I have a join table that contains flags for a relationship between two models that can be demonstrated as such:
(int) id | (int) user_id | (int) video_id | (bool) hasRated | (bool) hasWatched | (bool) hasDownloaded
The first column is just the regular table id. The next two (user_id and video_id) are the typical HABTM foreign keys to the user table and video table. The following 3 flags are indicators of the relationship between the two models (has this video been watched? has it been rated? etc.)
I am currently building my own queries and this is my boot-strap setup. My end goal is to add attributes between the relationship of two models. Is this the correct way to approach this, or is there a more Cakey way to do it?

For maintain a HABTM you can maintain join table. The cake convention for join table for HABTM is:
This new join table's name needs to include the names of both models
involved, in alphabetical order, and separated with an underscore ( _
). The contents of the table should be two fields, each foreign keys
(which should be integers) pointing to both of the primary keys of the
involved models. To avoid any issues - don't define a combined primary
key for these two fields, if your application requires it you can
define a unique index. If you plan to add any extra information to
this table, it's a good idea to add an additional primary key field
(by convention 'id') to make acting on the table as easy as any other
model.
So its seems just fine for you. But you must follow the naming convention.
For more detail see docs.

Eureka.
In 2.x, cake will automagically create a model for you and recognize the relationship.
$this->Video->UserVideo will generate a model that fits the HABTM join table users_videos. You can then interact with it just like a regular model and it will update the database.

Related

Should a junction table for a many-to-many relationship have a surrogate primary key?

I'm working with mySQL and Laravel.
I have two tables: users and groups. Each has an id field and a name field.
I want users to be able to belong to many groups and vice versa; so I'm setting up a many to many relationship with a junction table: group_user.
My first thought is that it only needs two fields: user_id and group_id, both Foreign Keys.
But something tells me I might need/want an id for this table too. I've seen it both ways in code examples across the internet and I just want to know what I would be missing by not having it.
I just can't think of how I would actually make use of it; since the table is just to relate two other tables.
The artificial primary key can be very useful sometimes in your code. For example when you would like to delete a record you can use the same pattern as in the case of other tables.
You have to pass only one id instead of the parts of the compound key.

Propel ORM three way many to many relationship

I am trying to add Propel 1.7 to a legacy php project as part of a refactor. I have generated the schema.xml file, and am now adding in the many-to-many relations.
However, there is a table that encodes a three-way many to many relationship, so it has three columns as its primary key, each of which refers to a different id column in a different table.
Basically, websites are a concept defined by a country and a language.
ads_to_websites:
ad_id | country_id | lang_id
refer to
ads->id
country->id
lang->id
Is this supported at all in Propel? I understand how to add foreign keys and primary keys to the schema.xml, but what happens when I want to do a join through the table? Can I use isCrossRef? The documentation is slightly vague, I find.
Or should I make a new table, so that we have
websites:
id | country_id | lang_id
refering to
country->id
lang->id
and
ads_to_websites:
website_id | ad_id
refering to
websites->id
ads->id
Which is possible and I think fits the data model, but a pain to implement (another project uses the same db).
Alternatively, would another ORM like Doctrine do this better?

How to model database where different users/roles are in a project table, while all of them have to belong to user table?

I am trying to model a database for my current project and I came across a new problem. I have a Project which is supervised by Supervisor, Coordinator and Company. So Project table has Supervisor.id as foreign key and so on. There is also Student table which contains Project.is as a foreign key (because many users can do a project). This is how it is right now. What I would like to do is to have a User table containing a column named type which allows me to see what the role of that particular user is (also student). Even though the table will contain many NULL entries, I will have far less redundant code.
However, the main reason I want to have one User table is that I am using CakePHP and it is not easy to have different models log in. Is this possible in a nice way?
Thanks
EDIT: Maybe I should say that every one of these roles will have different permissions.
I see three tables: USER, GROUP, and ROLE.
Users would be assigned to groups, and groups given roles. I think you need three, not one.
And cardinality matters: I can see where a USER could be assigned to many GROUPS; a GROUP could have many USERS; a ROLE could be assigned to several GROUPS; and a GROUP could have many ROLES. There are many to many JOIN tables as well:
USER <-> USER_GROUP <-> GROUP <-> GROUP_ROLE <-> ROLE
This is normalized - nothing is repeated this way. USER, GROUP, and ROLE have primary keys. The JOIN table primary key is a composite of the two IDs in each row.
It depend on how you will use your associations.
Why not
For example: if you use relation to output data later and you sure, that you database scheme will not changed, than ... why not? your main targets: quality of code and speed of development, so, not matter how much columns with null you will have.
But
if you not sure in your requirements or plan to extend database scheme you can use two columns
supervisor_model
supervisor_id
which will store apropriate values: Company, 77 (I mean that 77 it's id of come Company )
Another approach
You can UUID for you supervisor_id so, it will be unique among several tables and you have not many NULL and extra columns.

Advice on database design for portfolio website

So I'm a visual designer type guy who has learned a respectable amount of PHP and a little SQL.
I am putting together a personal multimedia portfolio site. I'm using CI and loving it. The problem is I don't know squat about DB design and I keep rewriting (and breaking) my tables. Here is what I need.
I have a table to store the projects:
I want to do fulltext searcheson titles and descriptions so I think this needs to be MyISAM
PROJECTS
id
name (admin-only human readable)
title (headline for visitors to read)
description
date (the date the project was finished)
posted (timestamp when the project was posted)
Then I need tags:
I think I've figured this out. from researching.
TAGS
tag_id
tag_name
PROJECT_TAGS
project_id (foreign key PROJECTS TABLE)
tag_id (foreign key TAGS TABLE)
Here is the problem I have FOUR media types; Photo Albums, Flash Apps, Print Pieces, and Website Designs. no project can be of two types because (with one exception) they all require different logic to be displayed in the view. I am not sure whether to put the media type in the project table and join directly to the types table or use an intermediate table to define the relationships like the tags. I also thinking about parent-types/sub-types i.e.; Blogs, Projects - Flash, Projects - Web. I would really appreciate some direction.
Also maybe some help on how to efficiently query for the projects with the given solution.
The first think to address is your database engine, MyISAM. The database engine is how MySQL stores the data. For more information regarding MyISAM you can view: http://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html. If you want to have referential integrity (which is recommended), you want your database engine to be InnoDB (http://dev.mysql.com/doc/refman/5.0/en/innodb-storage-engine.html). InnoDB allows you to create foreign keys and enforce that foreign key relationship (I found out the hard way the MyISAM does not). MyISAM is the default engine for MySQL databases. If you are using phpMyAdmin (which is a highly recommended tool for MySQL and PHP development), you can easily change the engine type of the database (See: http://www.electrictoolbox.com/mysql-change-table-storage-engine/).
With that said, searches or queries can be done in both MyISAM and InnoDB database engines. You can also index the columns to make search queries (SELECT statements) faster, but the trade off will be that INSERT statements will take longer. If you database is not huge (i.e. millions of records), you shouldn't see a noticeable difference though.
In terms of your design, there are several things to address. The first thing to understand is an entity relationship diagram or an ERD. This is a diagram of your tables and their corresponding relationships.
There are several types of relationships that can exist: a one-to-one relationship, a one-to-many relationship, a many-to-many relationship, and a hierarchical or recursive relationship . A many-to-many relationship is the most complicated and cannot be produced directly within the database and must be resolved with an intermittent table (I will explain further with an example).
A one-to-one relationship is straightforward. An example of this is if you have an employee table with a list of all employees and a salary table with a list of all salaries. One employee can only have one salary and one salary can only belong to one employee.
With that being said, another element to add to the mix is cardinality. Cardinality refers to whether or not the relationship may exist or must exist. In the previous example of an employee, there has to be a relationship between the salary and the employee (or else the employee may not be paid). This the relationship is read as, an employee must have one and only one salary and a salary may or may not have one and only one employee (as a salary can exist without belonging to an employee).
The phrases "one and only one" refers to it being a one-to-one relationship. The phrases "must" and "may or may not" referring to a relationship requiring to exist or not being required. This translates into the design as my foreign key of salary id in the employee table cannot be null and in the salary table there is no foreign key referencing the employee.
EMPLOYEE
id PRIMARY KEY
name VARCHAR(100)
salary_id NOT NULL UNIQUE
SALARY
id PRIMARY KEY
amount INTEGER NOT NULL
The one-to-many relationship is defined as the potential of having more than one. For example, relating to your portfolio, a client may have one or more projects. Thus the foreign key field in the projects table client_id cannot be unique as it may be repeated.
The many-to-many relationship is defined where more than one can both ways. For example, as you have correctly shown, projects may have one or more tags and tags may assigned to one or more projects. Thus, you need the PROJECT_TAGS table to resolve that many-to-many.
In regards to addressing your question directly, you will want to create a separate media type table and if any potential exists whatsoever where a project is can be associated to multiple types, you would want to have an intermittent table and could add a field to the project_media_type table called primary_type which would allow you to distinguish the project type as primarily that media type although it could fall under other categories if you were to filter by category.
This brings me to recursive relationships. Because you have the potential to have a recursive relationship or media_types you will want to add a field called parent_id. You would add a foreign key index to parent_id referencing the id of the media_type table. It must allow nulls as all of your top level parent media_types will have a null value for parent_id. Thus to select all parent media_types you could use:
SELECT * FROM media_type WHERE parent_id IS NULL
Then, to get the children you loop through each of the parents and could use the following query:
SELECT * FROM media_type WHERE parent_id = {$media_type_row->id}
This would need to be in a recursive function so you loop until there are no more children. An example of this using PHP related to hierarchical categories can be viewed at recursive function category database.
I hope this helps and know it's a lot but essentially, I tried to highlight a whole semester of database design and modeling. If you need any more information, I can attach an example ERD as well.
Another posibble idea is to add columns to projects table that would satisfy all media types needs and then while editting data you will use only certain columns needed for given media type.
That would be more database efficient (less joins).
If your media types are not very different in columns you need I would choose that aproach.
If they differ a lot, I would choose #cosmicsafari recommendation.
Why don't you take whats common to all and put that in a table & have the specific stuff in tables themelves, that way you can search through all the titles & descriptions in one.
Basic Table
- ID int
- Name varchar()
- Title varchar()
etc
Blogs
-ID int (just an auto_increment key)
-basicID int (this matches the id of the item in the basic table)
etc
Have one for each media type. That way you can do a search on all the descriptions & titles at the one time and load the appropriate data when the person clicked through the link from a search page. (I assume thats the sort of functionality you mean when you say you want to be able to let people search.)

Proper data-structure to maintain one-to-many relationship

I want to represent documents in a database. There are several different types of documents. All documents have certain things in common, but not all documents are the same.
For example, let's say I have a basic table for documents...
TABLE docs (
ID
title
content
)
Now let's say I have a subset of documents that can belong to a user, and that can have additional info associated with them. I could do the following...
TABLE docs (
ID
userID -> users(ID)
title
content
additionalInfo
)
...however this will result in a lot of null values in the table, as only some documents can belong to a user, not all. So instead I have created a second table "ownedDocs" to extend "docs":
TABLE ownedDocs (
docID -> docs(ID)
userID -> users(ID)
additionalInfo
)
I am wondering: Is this the right way to do it? (I am worried because while everything is in one table, I have a one-to-many relationship between docs and users. However, by creating a new table ownedDocs, the datastructure looks like I have a many-to-many relationship between docs and users - which will never occur.)
Thanks in advance for your help
the datastructure looks like I have a many-to-many relationship between docs and users - which will never occur.
Understood, then you need to have the userid in the docs table to ensure a one-to-many relationship (one user, potentially many documents).
I don't see the harm in additional info columns being null if a document is not associated to a particular user, noted by the userid column being null. Splitting the additional info to another table still means a one-to-one relationship, so you're best off using one table with doc, user and additional info...
"by creating a new table ownedDocs,
the datastructure looks like I have a
many-to-many relationship between docs
and users - which will never occur.)"
If you make OwnedDocs.DocId the primary key it will be quite clear that a 1:N relationship is impossible.
The modelling of zero or one to one relationships is tricky. If we have just the one sub-type then the single table with NULL columns is a reasonable approach. However it is good practice to ensure that the sub-types attributes are only populated when appropriate. In the given example that would mean a check constraint to enforce this rule:
check (userID is not null or AdditionalInfo is null)
Or maybe even this rule:
check ( (userID is not null and AdditionalInfo is not null)
or (userID is null and AdditionalInfo is null) )
The relationship between attributes won't show up in an ERD (unless you use a naming convention). For sure, the mandatory nature of AdditionalInfo for owned documents won't be obvious in the second case.
Once we have several such sub-types the case for separate tables becomes compelling, especially if the sub-types constitute an arc e.g. a Document can be a FinancialDocument or a MedicalDocument or a PersonnelDocument but not more than one category. I once implemented such a model using a single table with lots of null columns, views and check constraints. It was horrible. Sub-type tables are definitely the way to go.
It depends on the level of normalization you want to achieve. Typically, based on the description you are providing, I would structure my DB like so:
table docs (id, title, content);
table users (id, ...);
table users_docs (doc_id, user_id);
table doc_info(doc_id, additional_info);
Someone correct me if I am wrong, but this should be 3rd normal form. This keeps the structure nice and clean and uses only the bits required to store the data as expected. You can store all elements independently but related where needed.
Depending on the nature of the additional information, you need to make some changes. For example, will the additional info ALWAYS correspond to a user? Will it always be supplied if the doc is associated to a user? If so, then you can add it to the users_docs table. But this should at least show you the normalization.

Categories