CakePHP Tree-Node-like behaviour on model - cakephp

I'm currently working on a project in which I'm going to need to create a binary-tree structure on a model. It's something similar to "A person can have 2 friends. Each of those 2 friends are persons as well, so they can also have 2 friends each one." Finally I will need to do a depth-first node search.
I want to do a depth search up to 4 levels, and I also want to know if any of the levels are uncompleted, for example:
person(root)
person(branch A) person(branch B)
person person person person
person person person person (no children) (only 1 child)person
as you can see, branch A is complete up to 4 levels. but branch B is not.
I'm guessing the way to know which branch is complete and which one isn't is to verify using empty() function on the array, something like:
$person = $this->Person->find('first', $conditions);
if(empty($person['Person']['friend_1']))
$uncomplete = true;
I'm missing the algorithm to iterate through nodes here, but what I really need is how do I retrieve 4 depths of hasOne / belongsTo (not sure which one is it), is it setting Model::recursive = 4?
Any suggestions are appreciated, thanks

in Cake we don't have recursive 4. recursive used for associated tables which are associated to current table (other meaning Model behavior) which can be between -1 to 2.
and also regarding to your table structure. please read the cake documents here to see which one one this is suits your table structure. In cake we have 3 kind of model structure. e.g. $actsAs = Tree or Containable or Translate
look at these question and article also:
Reference 1
Reference 2
Reference 3

Related

How to query for multiple vertices and counts of their relationships in Gremlin/Tinkerpop 3?

I am using Gremlin/Tinkerpop 3 to query a graph stored in TitanDB.
The graph contains user vertices with properties, for example, "description", and edges denoting relationships between users.
I want to use Gremlin to obtain 1) users by properties and 2) the number of relationships (in this case of any kind) to some other user (e.g., with id = 123). To realize this, I make use of the match operation in Gremlin 3 like so:
g.V().match('user',__.as('user').has('description',new P(CONTAINS,'developer')),
__.as('user').out().hasId(123).values('name').groupCount('a').cap('a').as('relationships'))
.select()
This query works fine, unless there are multiple user vertices returned, for example, because multiple users have the word "developer" in their description. In this case, the count in relationships is the sum of all relationships between all returned users and the user with id 123, and not, as desired, the individual count for every returned user.
Am I doing something wrong or is this maybe an error?
PS: This question is related to one I posted some time ago about a similar query in Tinkerpop 2, where I had another issue: How to select optional graph structures with Gremlin?
Here's the sample data I used:
graph = TinkerGraph.open()
g = graph.traversal()
v123=graph.addVertex(id,123,"description","developer","name","bob")
v124=graph.addVertex(id,124,"description","developer","name","bill")
v125=graph.addVertex(id,125,"description","developer","name","brandy")
v126=graph.addVertex(id,126,"description","developer","name","beatrice")
v124.addEdge('follows',v125)
v124.addEdge('follows',v123)
v124.addEdge('likes',v126)
v125.addEdge('follows',v123)
v125.addEdge('likes',v123)
v126.addEdge('follows',v123)
v126.addEdge('follows',v124)
My first thought, was: "Do we really need match step"? Secondarily, of course, I wanted to write this in TP3 fashion and not use a lambda/closure. I tried all manner of things in the first iteration and the closest I got was stuff like this from Daniel Kuppitz:
gremlin> g.V().as('user').local(out().hasId(123).values('name')
.groupCount()).as('relationships').select()
==>[relationships:[:]]
==>[relationships:[bob:1]]
==>[relationships:[bob:2]]
==>[relationships:[bob:1]]
so here we used local step to restrict the traversal within local to the current element. This works, but we lost the "user" tag in the select. Why? groupCount is a ReducingBarrierStep and paths are lost after those steps.
Well, let's go back to match. I figured I could try to make the match step traverse using local:
gremlin> g.V().match('user',__.as('user').has('description','developer'),
gremlin> __.as('user').local(out().hasId(123).values('name').groupCount()).as('relationships')).select()
==>[relationships:[:], user:v[123]]
==>[relationships:[bob:1], user:v[124]]
==>[relationships:[bob:2], user:v[125]]
==>[relationships:[bob:1], user:v[126]]
Ok - success - that's what we wanted: no lambdas and local counts. But, it still left me feeling like: "Do we really need match step"? That's when Mr. Kuppitz closed in on the final answer which makes copious use of the by step:
gremlin> g.V().has('description','developer').as("user","relationships").select().by()
.by(out().hasId(123).values("name").groupCount())
==>[user:v[123], relationships:[:]]
==>[user:v[124], relationships:[bob:1]]
==>[user:v[125], relationships:[bob:2]]
==>[user:v[126], relationships:[bob:1]]
As you can see, by can be chained (on some steps). The first by groups by vertex and the second by processes the grouped elements with a "local" groupCount.

How do I add a HABTM record in CakePHP?

I am new to CakePHP and have a fairly basic question.
I have two tables : books and users. books and users have a habtm relationship. I have created the MVC for the above.
Now when a user logs into the system, I want the user to be able to reserve a book (ie an entry in books_users), by looking at the results of the 'index' action. What is the API to be used?
$this->Book->save() does not seem appropriate as we aren't creating a book. We only want an association between an existing book and the logged-in user.
I am trying to avoid, retrieving $this->Book, iterating manually through the sub-array User, creating a new sub-array and saving the whole thing back. I am sure there must be a simpler way.
Adapted from Chuck's answer, unsure why edit was pushed back.
In app/Model/Book.php
class Book extends AppModel {
/************************************************************************
* If you want your multiple assoc. to work you must set unique to *
* false, otherwise when you save an entry it will enforce unique *
* on book ID and subsequently your associations will delete previously *
* saved associations, acting more like "User HasMany Books". *
************************************************************************/
var $hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'unique' => false
));
public function addUser($bid, $uid) {
$this->data['User']['id'] = $uid;
$this->data['Book']['id'] = $bid;
$this->save($this->data);
}
}
In app/Controller/BooksController.php (or UsersController)
$this->Book->addUser($bid, $uid);
Fat Models / Skinny Controllers. Allows duplicate entries (you need to constrain limits and check for duplicates, otherwise default behaviour makes HMBTM difficult). Does exactly what you want it to, you just need to supply book and user id.
CakePHP doesn't tend to encourage complex associations, and the reason this is because HMBTM is just a convenience and care should be taken when mixing it with other associations, as per the link provided below, self defined associations are more predictable than HMBTM in CakePHP
http://book.cakephp.org/2.0/en/models/saving-your-data.html#what-to-do-when-habtm-becomes-complicated
You simply need to save a record to Book or User that contains the ids of both and it will insert it into the HABTM table.
$this->data['User']['id'] = {USER_ID};
$this->data['Book']['id'] = {BOOK_ID};
$this->Book->save($this->data);
Look at the HABTM table and you will find the record.
did you bake your application? This (basic) functionality will be provided to you for you to adapt.
In short - take the id of the book and the id of the user. Save it to your books_users table.
id | book_id | user_id
1 1 1
2 2 2
3 2 3
If you have set your associations up correctly, when you request a user, all their books will be returned from the join table.
Your logic will need to deal with - number of books available,if a person can reserve more than one book at once...
http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-related-model-data-habtm
Has an example.

basic DB polymorphism link

I have a kind of graph, composed of 5 kinds of objects, 1 of them is a link type of object, and the others are the 4 different types of 'nodes' between links.
Each of the 5 is so different that will have a different table.
Each 'node' will have 0..N links 'below', while each link will have exactly 1 'node' below (but of any of 4 kinds). Only things below need to be stored.
So my question is this, is it better in the link table to create 4 columns (node1, node2, node3, node4), and leave 3 of them NULL, the other containing the id of the node, or is it better to have 2 columns (nodetype, nodeId), where 1 tells wich table to search and the other the Id.
For the 0..N relationship I will have to create a new table I supose, but maybe just one to be shared by the 4 types of node.
Read the following for some ideas on how to handle polymorphism in DBs
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/inheritance.html

cakePHP model associations (retrieving data deeper)

I tried to find this everywhere but I cant. Here is my problem:
Lets say I have models associated like this:
Student (has many Memberships)
Membership (belongs to Student and TeacherCourse)
TeacherCourse (belongs to Teacher and Course)
Teacher (has many TeacherCourses)
Course (has many TeacherCourses)
When I use membershipsController to send all membership to the view, I get only for example $membership['Membership']['id'] or $membership['TeacherCourse']['id'] BUT NOT $membership['TeacherCourse']['Teacher']['id']...
Which means I get info on Memberships and TeacherCourse because they are directly associated. My question is how can I also get info about Teachers or Courses directly?
You could increase the model's recursion level to obtain deeper associations (set $this->Membership->recursive to 2 or 3), but in general it's better to use the Containable behavior so you can choose which associations you want to retrieve. For example this would return memberships with associated TeacherCourse and Teacher models:
$membership = $this->Membership->find('all', array(
'contain'=>array(
'TeacherCourse'=>array(
'Teacher'=>array()
),
));
See also the full documentation of the Containable behavior at http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
Scratching my head for a few minutes on this one when I realised you need to add the following to the Model first:
public $actsAs = array('Containable');
Just adding the contain array in the controller may not return the variables you're looking for in the view without the above.

What is an appropriate data structure and database schema to store logic rules?

Preface: I don't have experience with rules engines, building rules, modeling rules, implementing data structures for rules, or whatnot. Therefore, I don't know what I'm doing or if what I attempted below is way off base.
I'm trying to figure out how to store and process the following hypothetical scenario. To simplify my problem, say that I have a type of game where a user purchases an object, where there could be 1000's of possible objects, and the objects must be purchased in a specified sequence and only in certain groups. For example, say I'm the user and I want to purchase object F. Before I can purchase object F, I must have previously purchased object A OR (B AND C). I cannot buy F and A at the same time, nor F and B,C. They must be in the sequence the rule specifies. A first, then F later. Or, B,C first, then F later. I'm not concerned right now with the span of time between purchases, or any other characteristics of the user, just that they are the correct sequence for now.
What is the best way to store this information for potentially thousands of objects that allows me to read in the rules for the object being purchased, and then check it against the user's previous purchase history?
I've attempted this, but I'm stuck at trying to implement the groupings such as A OR (B AND C). I would like to store the rules in a database where I have these tables:
Objects
(ID(int),Description(char))
ObjectPurchRules
(ObjectID(int),ReqirementObjectID(int),OperatorRule(char),Sequence(int))
But obviously as you process through the results, without the grouping, you get the wrong answer. I would like to avoid excessive string parsing if possible :). One object could have an unknown number of previous required purchases. SQL or psuedocode snippets for processing the rules would be appreciated. :)
It seems like your problem breaks down to testing whether a particular condition has been satisfied.
You will have compound conditions.
So given a table of items:
ID_Item Description
----------------------
1 A
2 B
3 C
4 F
and given a table of possible actions:
ID_Action VerbID ItemID ConditionID
----------------------------------------
1 BUY 4 1
We construct a table of conditions:
ID_Condition VerbA ObjectA_ID Boolean VerbB ObjectB_ID
---------------------------------------------------------------------
1 OWNS 1 OR MEETS_CONDITION 2
2 OWNS 2 AND OWNS 3
So OWNS means the id is a key to the Items table, and MEETS_CONDITION means that the id is a key to the Conditions table.
This isn't meant to restrict you. You can add other tables with quests or whatever, and add extra verbs to tell you where to look. Or, just put quests into your Items table when you complete them, and then interpret a completed quest as owning a particular badge. Then you can handle both items and quests with the same code.
This is a very complex problem that I'm not qualified to answer, but I've seen lots of references to. The fundamental problem is that for games, quests and items and "stats" for various objects can have non-relational dependencies. This thread may help you a lot.
You might want to pick up a couple books on the topic, and look into using LUA as a rules processor.
Personally I would do this in code, not in SQL. Each item should be its own class implementing an interface (i.e. IItem). IItem would have a method called OkToPurchase that would determine if it is OK to purchase that item. To do that, it would use one or more of a collection of rules (i.e. HasPreviouslyPurchased(x), CurrentlyOwns(x), etc.) that you can build.
The nice thing is that it is easy to extend this approach with new rules without breaking all the existing logic.
Here's some pseudocode:
bool OkToPurchase()
{
if( HasPreviouslyPurchased('x') && !CurrentlyOwns('y') )
return true;
else
return false;
}
bool HasPreviouslyPurchased( item )
{
return purchases.contains( item )
}
bool CurrentlyOwns( item )
{
return user.Items.contains( item )
}

Resources