I want to create a Class Table Inheritance model in CakePHP.
I would like to have a Model called something like ProductBase with the table product_bases to hold all the base information every product should have, like upc, price, etc.
Then have specific product type models extend that. For example ProductRing with the table product_rings to hold specific ring information like ring_size, center_stone, etc.
Then if I retrieve data directly from the ProductBase model, have it pull all types:
// pull all product types
$this->ProductBase->find('all');
Or find specific types only:
// pull only Rings or descendants of the Ring type.
$this->ProductRing->find('all');
Is anything like this possible in CakePHP? If not, what should I be doing instead?
What is the proper Cake way of doing something like this?
I worked with CakePHP for two years, and found no satisfactory solution for this, so one day I wrote a solution for it. I built a new kind of ORM that work as a plugin on top of CakePHP 2.x. I called it "Cream".
It works similar to the entities of CakePHP 3.0, but in addition supports multi table inheritance. It also supports very convenient data structure browsing (lazy loading) and is very easy to configure. In my opinion it is more powerful than what CakePHP 3.0 offers right now. Data structure browsing works as follows:
$entity = new Entity('SomeModel', $somePrimaryKeyValue);
$foo = $entity->RelatedModel()->YetAnotherRelatedModel()->someProperty();
However, it is important to notice, that in Cream, each entity object is a compund of a series of models and primary key values that are merged together. At least in the case where model inheritance is used. Such a compound looks like:
[<'SomeConcreteModel', primaryKeyValueA>, <'IntermediaryModel', primaryKeyValueB>, <'BaseModel', primaryKeyValueC>]
It is important to notice that you can pick up this entity by any of the given model/primaryKeyValue combinations. They all refer to the same entity.
Using this you can also solve your problem. You can use standard CakePHP find methods to find all primary key values you want from the base model, or you can use the find methods models that inherit from it, and then go along and create the entities.
You set up the chain of inheritance/extension by simply writing in your model class:
public $extends = 'YourBaseModel';
In addition you also needs to setup an ordinary CakePHP relationship between the models (hasOne or belongsTo). It works just like in normal OOP, with a chain of models that inherit from their bases. If you just use vanilla CakePHP you will just notice that these models are related, but when you start using the Cream interface, all entities merge model/primaryKeyValue pairs into one single object.
Within my github repository there is a powerpoint file that explain most of the basic features.
https://github.com/erobwen/Cream
Perhaps I should fork the CakePHP project and make a pull request, but for now It is a separate repository. Please feel free to comment or participate in developing "Cream".
Also, for those suggesting that it is best to just "work with the CakePHP flow as intended" I would argue the following. Common estimates suggest that C programs are 2.5 times bigger than the C++ counterpart. Given that the only feature that separates these languages is the OOP with inheritance etc, we can deduce that the lack of proper OOP with inheritance etc requires the programmer to do 150% additional work with repetition code etc. Therefore I would argue that a proper model inheritance mechanism in CakePHP is very much needed. Cream is an attempt at this.
You are referring to an ARC relationship (or at least a variation of it). Cake does not handle these types of relationships on the fly. This means you will have to implement your own logic to handle this.
The other option is to categorize the products. If the product can fit into multiple categories, then you will want a HABTM categories for each product. Otherwise, you can use a category column. I suspect it will be a HABTM you are looking for.
PRODUCTS: The table that holds the
products.
CATEGORIES: The list of categories
any given product can belong to.
CATEGORIES_PRODUCTS: The link between
each product and their various
categories.
TYPE: This is the flag that will
define the type of product (i.e.
ring, shoe, pants, etc.)
Then when you want ALL products, you query the products table. When you want a slice of the products (i.e. Rings) you select all the products that belongs to the RING category.
Now, we need to address the information about the product. For example, not all information will apply to every product. There are a number of ways to do this.
You can build multiple tables to
hold the product information. When
you pull a product of a given type,
you pull its companion information
from the table.
Store the information in a text
field as serialized data. All of the
information can be defined in a
settings var and then you can use
the serialized data to map to the
information.
I hope this helps. Happy coding!
Related
In my current design, I have app_group, student and group_article:
To structurally ensure that a group_article is only associated with a student from that same group, the foreign keys "publisher" and "app_group" are taken from the join entity group_member (1) as opposed to having them issued from student and app_group individually. This way, someone with the right to insert new records into the database cannot introduce incoherent data such as adding an article that have been written by a student that isn't even in that group which would be poor design. Now, I want generalize this approach into multiple students or multiple groups. I now have group_message, group_message_in and group_message_out which is an inheritance chain (group_message is the base which is an abstract entity in Symfony, and both group_message_in and group_message_out extend it):
Initially, I was planning to embed the group foreign key on the base class (group_message) and have the sender/recipient (respectively on group_message_out and group_message_in) be taken from student directly:
However, this will leave the database vulnerable to incoherence as per the first example, eg: student from group A can be associated with a message that targets student from group B which is not desirable (only students from the same group can exchange group_message).
I'm well aware that I can amend this risk in code but I want a similar solution to (1) and to know if this is achievable with Doctrine since MySQL itself might have ways of solving a similar problem that aren't supported by Doctrine.
A relational solution to your problem would look something like this:
The integrity that you seek would be achieved by the PK-FK relationships and by assigning a student to a group using the groupName colums.
Your question then becomes something like "How can I use Doctrine to do the same thing?"
To the best of my knowledge Doctrine uses a set of PHP libraries to create what its proponents call a "persistence layer" that stores what it calls "Entities". With Doctrine, the term "Entity" is a synonym for "Class" in the OO paradigm.
In other words Doctrine stores classes in the data layer.
And now we can see the problem.
A relational schema is a structure of relations which is a completely different kind of artefact than a collection of classes.
The OO/Relational divide has been called an "impedance mismatch". Unfortunately this term obscures more than it reveals.
To quote from the Wikipedia article: "There have been some attempts at building object-oriented database management systems (OODBMS) that would avoid the impedance mismatch problem. They have been less successful in practice than relational databases however, partly due to the limitations of OO principles as a basis for a data model."
I suggest that you also review Ted Neward's article "The Vietnam of Computer Science."
This new answer shows the object-role model, the relational schema that it generates and the logic that is implied by the new constraint (shown by the red arrow)
The object- role model.
This is the logic that is asserted by the fact type Student(.id) is a member of Group(.name)
Now as the domain expert, you can read this verbalization and tell me whether it is True or False in your domain.
Please note that all I did as the modeler, was to change the constraint (shown by the red arrow) and the ORM tool called NORMA generated the new verbalization that you see here.
When the domain expert agrees that the model conforms to the requirements then it takes a few seconds to generate the SQL DDL that can then be used to create a new database schema in an RDBMS.
This is an abstract description of my problem without any specific technology/framwork.
I would like to know what the best practice is to model the following (toy)
example: Suppose I am selling toys. I have f.e. teddy bears and small elephants
and zebras. They share the following attributes:
price
number of eyes
fluffyness-rating
name
description
So I would like to create a table "toy" that stores this information and makes
it easy, f.e. to query the 10 cheapest toys.
I also want to store the animals explicitly, with all the information I get
from the manufacturer, let's say their weight, or how many colors they have.
This includes information that is only given for a subset of animals, f.e. the
elephants have a "number of tusks", while the zebras do not. There are two
reasons for that:
I might be using some of the common information (f.e.: weight) in the toys
table at a later point.
maybe I am going to run a different service that only lists animals with tusks.
My problem is: I would like to use reference fields, so a toy only has
references to some of the fields in one table, f.e. the zebra table.
If it is a zebra, it can not refer to the elephant table.
But every toy has to have the common attributes I mentioned above (price,
number of eyes, …).
And specificly, I planned to use django and it's ORM.
I know that I could join over all the different tables and let them implement
some of the common names, but a) I want to be able to add other types of
elements (f.e. via plugins) and b) I would like to assure that every model
implements the common names!
Also I could use a nosql database, that would be very flexible, but I am
interested in solving this problem with something traditional (to learn
something).
(Final remark: I am more interested in how you would solve such a problem. And which nice properties your solution has. I am sure I described a very fundamental problem that is part of the bigger question: How should I design my application, which could be best answered by working thoroughly through some book …)
I am going to model an abstract base class (that has f.e. a 'name') and then the class that creates f.e. the zebras is in charge of mapping from the custom 'zebra_name' to the 'name' of the abstract base class. That solves all my problems, I guess :).
Source: Django models.Model superclass
Edit: this solves the problem how I can design the DB, but not how I can query it, since ABC can not be queried.
In general it is not possible to query abstract base classes:
How to query abstract-class-based objects in Django? specificly https://stackoverflow.com/a/9981317/3750254 seems to answer the problem: Just use inheritance and select the subclasses that match. However, wouldn't that give me all the subclasses (with all the attributes I might not need)?
I have a site written in cakephp with a mysql database.
Into my site I want to track the activities of every users, for example (like this site) if a user insert a product I want to put this activity into my database.
I have 2 ways:
1) One table called Activities with:
- id
- user_id
- title
- text
- type (the type of activity: comment, post edit)
2) more table differenced by activities
- table activities_comment
- table activities_post
- table activities_badges
The problem is when I go to the page activities of a user I can have different type of activities and I don't know which of this solution is better because a comment has a title and a comment, a post has only a text, a badge has an external id to its table (for example) ecc...
Help me please
I'm not familiar with CakePHP, but from purely database perspective your data model should probably look similar to this:
The symbol denotes category (aka. inheritance, subclass, subtype, generalization hierarchy etc.). Take a look at "Subtype Relationships" in ERwin Methods Guide for more info.
There are generally 3 strategies for implementing the category:
All types in single table. This requires a lot of NULLs and requires CHECKs to make sure separate subtypes are not inappropriately "intermingled".
All concrete types in separate tables (excluding the base, which is ACTIVITY in your case), which means common fields and relationships must be repeated in all child tables.
All types in separate tables (including the base). This implementation requires a little more JOINing, but is flexible and clean. It should be your default, unless there are strong reasons against it.
I have a site nearing structural completion. Essentially, it's made up of Users and Photos. However, Photos have a LOT of HABTM relationships, most of which are different kinds of tags, and others being categories, collections, etc...
My client would like to be able to have a conditional CMS based off the content being viewed for different promotional purposes or whatnot. For example, if a user is browsing the Category "Leprechauns", he may want to show html content about St. Patrick's Day or something. He may even want to easily include the browing user's name, if available. In which case, it'd be nice to have some sort of templating system. The part that makes this tricky is that these are small blocks of HTML for something like a 300x300 space in the sidebar and is NOT the primary content.
I've looked into refinery, but it seems to be too much of a complete solution. I've also thought about building in from scratch, but I don't know where to begin with treating dynamically generated pages (such as a new Category) as a recognizable object in Rails. I'd like to keep it more global than a slew of HABTM relationships, but it's looking like that might be where I'm heading. Ideally, he'd be able to make some HTML and check off "I'd like this to show on x and y Category page and b and z Collection page and for the tag Foo and for the tag bar as well as on my homepage"
Any suggestions? I'm open to finding a gem or getting a solid start on a homebrew.
UPDATE
I'm thinking of setting up some models Like the following:
cms_contents
============
id:integer
contents:text
timestamps
cms_associations
================
id:integer
cms_content_id:integer
model_id:integer
record_id:integer #optional
secondary_model_id:integer #optional
secondary_record_id:integer #optional
models
======
id:integer
name:string
Where:
cms_content would contain the HTML.
cms_associations would be an ActiveRecord model using has_many, :through
models would contain a the models the CMS content could be associated with
if record_id is not given, it associates with the model index
if record_id is given, it associated with the record of the provided model with that
id
if secondary information is given, a combination is created for other many to many
relationships
eg:
User has_many :photos
Browsing Photos for User(10)
model_id => #User model Id
record_id => 10
secondary_model_id => #Photo model id
So you could customize the content for browsing photos for specific users such as featured users or companies that may pay for advertising.
Any thoughts on this structure?
It's actually not that complicated. This is what you should do:
Introduce new object in the DB, name it htmlchunks
For every object that you'd want to associate with htmlchunks, create a join table and define habtm relationship
Modify your sidebar (for objects that have association with htmlchunks) in a way that they recognize whether there's htmlchunk associated with this particular object. For example, let's say you have a htmlchunk called "April Fools' Day" that contains simple html paragraph. In the sidebar, you'd just ask something like #category.htmlchunk? and output according to the result. You'd ideally place all this in a partial
As for creation of the htmlchunks you'd just offer simple wisiwig editor and ability to select any number of tags, categories, whatever. You'd create habtm entries between them and htmlchunks during the creation process
Let me know if you have further questions in the comments.
It's almost as if the almighty Ryan Bates is looking out for me, but the latest Railscast is on CopyCopter, which does exactly what I'm looking for, and runs as a separate app, so I don't have to worry about bogging down my main app. I suggest anyone who finds this to watch the screencast, as it seems pretty impressive.
http://railscasts.com/episodes/336-copycopter?autoplay=true
I am currently busy implementing a basic Asset Management System. It will not be very complicated. Simply something to keep track of any asset with it's name, serial number, parts number and type etc. The problem I have however, is that I want to incorporate books as well. Unfortunately, books have a very different structure than normal assets (for example title, authors, isbn codes etc.).
I would like some insight from the community as to what design they think is best. Incorporate books in asset management (and if so, how should the database design look), or should I simply write a completely seperate, independant Library module (maybe with some functionality to export a book to the Asset Management System [with fewer / other fields]).
Thanks!
EDIT: Something else that is possible is to make the capture screen dynamic, so the user can specify the fields and the values. This can then be stored in as XML in the database. But his would not be my preferred way of doing it.
EDIT 2: I forgot to mention, I am very bound by the technologies that I may use. These are MySQL, GWT, Hibernate and Spring (no Spring transactions).
One approach could be to use a document style no-sql database (such as Mongo) to store the assets. That way each different type of asset can easily have its own set of fields without requiring additional tables, etc.
Basically what I'm picturing is pseudo-code similar to:
class Asset
{
int AssetNumber;
int AssetType;
string Description;
// etc.
}
class BookAsset : Asset
{
// book-specific fields
}
class ElectronicsAsset : Asset
{
// electronics-specific fields
}
// etc.
So additional asset types can just be additional derived classes. Then each asset would be written to the document database as its own distinct document, and retrieved by its asset number (or searched for based on the fields it contains, etc.) or name or however it's stored.
This would give you a quick and easy system with the flexibility you'll likely want as you track additional assets, or additional information about existing assets.
Edit based on your edit: User-defined fields should work just fine with this. You can set it up as some kind of key/value dictionary on the object, or even just add the fields to the object itself if using a more dynamic language. The "base asset" would be composed of the fields which are absolutely required, the rest can be more loosely-defined, conditionally required, user-specified, etc.
It makes sense to separate the general notion of an asset from the specifics of each type of asset you want to be able to incorporate. Typically, this would take the form of a master Asset table, with different tables for each distinct type of asset you wish to include, i.e. Book, Hardware, Furniture. The structure might look like this:
Asset(AssetId, Description, Comments)
HardwareAsset(HardwareAssetId, AssetId, SerialNumber, ...)
BookAsset(BookAssetId, AssetId, ISBN, Publisher, Author, ...)
Where AssetId in both HardwareAsset and BookAsset is a foreign key to the Asset table. That way, you can keep track of different assets and group them together when it should matter.
EDIT: Alternatively, you can create a key - value table to store values for individual objects, which could look like this:
AssetValue(AssetValueId, AssetId, Key, Value)
However, this is a cumbersome solution that, while still providing for searchable fields, will quickly bloat your database. To mitigate the problem you can limit the field size depending on your requirements. I do not suggest serializing the dictionary inside a single field, as this will bloat your database even more.
From the technology constraints I would suggest keeping the modules separate.
Yes on the main table you can flag what type of asset it is. So if it
is a book asset them a foreign key can link it to the book items. this
way you will not waste space on those assets that do not have these items.