Cakephp display other model associated with foreign key - foreign-keys

I have the models Item and Actor with a HABTM relation (working fine).
Now, I added the Role model, wich holds the actorid, itemid (as foreign key) and the actors' role.
In my Item view, I'd like to display the actor and its' role.
I have set recursive =2.
When I debug $items, I receive all the roles belonging to that actor.
Instead I'd like only the role displayed, that contains the viewed itemid AND actorid.
I believe I might need to tweak my models but don't know how.. Any suggestions?
Item Model
public $hasAndBelongsToMany = array(
'Actor' => array(
'className' => 'Actor',
'joinTable' => 'item2actor',
'foreignKey' => 'item_id',
'associationForeignKey' => 'actor_id',
'unique' => 'true'
));
public $hasMany = array(
'Role' => array(
'className' => 'Role',
'foreignKey' => 'actorid',
));
Actor Model
public $hasAndBelongsToMany = array(
'Item' => array(
'className' => 'Item',
'joinTable' => 'item2actor',
'foreignKey' => 'actor_id',
'associationForeignKey' => 'item_id',
'unique' => 'true'));
public $hasMany = array(
'Role' => array(
'className' => 'Role',
'foreignKey' => 'actorid',
));

Instead of using a -very large- query and recursive I suggest using Containable behavior with which it's much easier to maintain the data you want to receive from DB.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html # Cake Book.

Related

Doctrine Module objectSelect setvalue not working

I have a simple question regarding Doctrine Modules Object Select.
I have a simple objectSelect form element
$this->add(array(
'name' => 'timezone',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'label' => _('Timezone:'),
'label_attributes' => array('class' => 'required'),
'object_manager' => $this->getEntityManager(),
'target_class' => 'Application\Entity\Timezones',
'property' => 'timezone',
'is_method' => true,
'find_method' => array(
'name' => 'FindAll',
),
),
));
Now I want to select a certain option as default, I have used the setValue method to do this but it is not working.
$this->get('timezone')->setValue(335);
Does anyone know why this is?
Many thanks in advance.
I figured out why it wasn't working.
In my controller I was binding my form to a Doctrine entity which was empty. This was overriding my values I set. I added the values in my controller after the form was bound and this fixed the issue.
$entityManager = $this->getEntityManager();
$site = new Sites($this->getServiceLocator());
$form = new AddSiteForm($this->getServiceLocator());
$form->setHydrator(new DoctrineObject($entityManager));
$form->bind($site);
$form->get('timezone')->setValue(335);
$form->get('currencyCode')->setValue('GBP');

How to pass ArrayCollection to DoctrineModule\Form\Element\ObjectSelect

I tried pretty much everything I found by searching here and at Google too but still no luck.
I have User entity with ManytoMany relation with Countries, here is it:
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Admin\Entity\Country", cascade={"persist", "remove"})
* #ORM\JoinTable(name="user_country_linker",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="country_id", referencedColumnName="id")}
* )
*/
protected $countries;
Now I'm trying to display DoctrineModule\Form\Element\ObjectSelect with allowed/ assigned countries only. I do have this list available by calling $this->zfcUserAuthentication()->getIdentity()->getCountries().
Is there any way to pass this ArrayCollection to ObjectSelect form element?
$this->add(array(
'name' => 'country',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'label' => 'Country',
'object_manager' => $em,
'target_class' => '\Admin\Entity\Country',
'property' => 'code',
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array(),
'orderBy' => array('id' => 'asc'),
),
),
'column-size' => 'sm-10',
'label_attributes' => array('class' => 'col-sm-2'),
'help-block' => 'Select country where the entity is present'
),
'attributes' => array(
'required' => false
)
));
Many thanks for the help, I really appreciate it!
How to fill a Dropdown in your controller is best described here: zf2 create select/drop down box and populate options in controller?. This is basically AlexP's solution.
If this is not what you are looking for, maybe the method described by this post can help you. At least it could help others like me that were looking for a solution like this: http://samsonasik.wordpress.com/2014/05/22/zend-framework-2-using-doctrinemoduleformelementobjectselect-and-custom-repository/
You basically create a custom reposity which holds a custom query to retrieve possible solutions:
namespace Your\Repository;
use Doctrine\ORM\EntityRepository;
class CountriesRepository extends EntityRepository
{
public function getPossibleCountries()
{
$querybuilder = $this->_em
->getRepository($this->getEntityName())
->createQueryBuilder('c');
return $querybuilder->select('c')//... define your query here
->getQuery()->getResult();
}
}
You can then refer to that method in your ObjectSelect:
$this->add(array(
'name' => 'continent',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'object_manager' => $this->entityManager,
'target_class' => 'Your\Entity\User',
'property' => 'contries',
'is_method' => true,
'find_method' => array(
'name' => 'getCountries',
),
),
));

How to use DoctineModule ObjectSelect based on custom repository method?

I have a table with a list of person that belong to different categories A and B for example. My problem is that i have a form with DoctrineModule ObjectSelect and i want to show in the ObjectSelect only the name of persons of Category A.
I find this https://github.com/doctrine/DoctrineModule/blob/master/docs/form-element.md#example-3--extended-version but the example is not clear for me and i don't know how to adapt it to my case.
Thank you.
excuse me for my english.
It's actually quite similar to the example you were looking at (I guess that's why there is no example for it), the only difference is that instead of using find/findBy/... you pass your custom repository name as name key, with code similar to this:
$this->add(array(
'name' => 'my-select-object',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'attributes' => array(
),
'options' => array(
'label' => 'My Label',
'object_manager' => $entityManager,
'target_class' => 'Application\Entity\MyEntity',
'property' => 'name',
'is_method' => true,
'find_method' => array(
'name' => 'myCustomRepositoryMethod',
'params' => array(
),
),
),
));
Also notice that your entity needs to know about repository existence, so make sure it uses this line:
#ORM\Entity(repositoryClass="Application\Entity\Repository\MyCustomRepository")
So when you open the form with this ObjectSelect it will call you repository method instead of loading the dropdown directly. That method should just return an array of entity objects which are then used by ObjectSelect to generate select element's options.

CakePHP 2.0 twitter-like follow button

I can't help myself and it's currently annoying, and yes, I used google a lot.
What I need:
A twitterlike follow button with the action to follow user.
What I already did:
Database
users table: id, username, password, ...
users_users table: id, user_id, follower_id
Code
In model User.php
public $hasAndBelongsToMany = array(
'Follower' => array(
'className' => 'User',
'joinTable' => 'users_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'follower_id',
'unique' => 'keepExisting',
)
);
In UsersController.php
public function follow() {
/*need help here*/
}
In Users\index.ctp
<?php if ($current_user['id'] != $user['User']['id']) echo $this->Html->link('Follow', array('action' => 'follow', $user['User']['id'])); ?>
Personally, I don't find hasAndBelongsToMany to be a good fit for situations like this. It's a good fit for when you want to display a list of checkboxes, or a select list, and allow the user to select/manage all their followings (or whatever the relationships might be) in one form.
It might just be my personal preference, but in situations like yours, where you're adding/deleting single links without worrying about any of the other links related to that user, I prefer to just create a separate 'Relationships' (or similarly named) Model / Controller, and consider the records as things in their own right, as opposed to just hasAndBelongsToMany links that are all sort of 'automagically' managed.
Here's how I'd do it:
Name your users_users table 'relationships'. And name the columns 'followed_by_id' and 'following_id' (or similar) to avoid any ambiguity as to which user is the follower / followee (if that was a word!).
In your users Model, you'd have these relationships:
var $hasMany = array(
'Followers' => array(
'className' => 'Relationship',
'foreignKey' => 'following_id',
'dependent'=> true
),
'FollowingUsers' => array(
'className' => 'Relationship',
'foreignKey' => 'followed_by_id',
'dependent'=> true
),
);
Then you'd have a Relationships model that looks something like this (the $belongsTo relationships are the important part):
<?php
class Relationship extends AppModel {
var $name = 'Relationship';
var $validate = array(
'followed_by_id' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'following_id' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
);
var $belongsTo = array(
'FollowedBy' => array(
'className' => 'User',
'foreignKey' => 'followed_by_id'
),
'Following' => array(
'className' => 'User',
'foreignKey' => 'following_id'
)
);
}
?>
And then in your Relationships controller, you'd have something like this:
function add($following_id = null) {
$this->Relationship->create();
$this->Relationship->set('followed_by_id',$this->Auth->User('id'));
$this->Relationship->set('following_id',$following_id);
if ($this->Relationship->save($this->data)) {
// all good
} else {
// You could throw an error here if you want
$this->Session->setFlash(__('Error. Please, try again.', true));
}
$this->redirect($this->referer());
}
Then to add relationships, you obviously just call the add method of your relationships controller.
NOTE: Ideally, since adding a relationship is changing the database, it ideally shouldn't be done with a GET request accessed by a regular URL. It should be done via submitting a form via POST. I know that seems overkill when it's so easy to just do it via a regular link with GET. I haven't bothered to use forms/POST in this example - but if you want to stick to best practices, that's what you should do. See this for more info: https://softwareengineering.stackexchange.com/questions/188860/why-shouldnt-a-get-request-change-data-on-the-server

Pretty HABTM List Entry

I have a Recipe, Item, and Units table/model. I have a HABTM relationship with Recipe and Item, and I get the default multiple-select box when adding/editing Recipe. (am using Bake for everything for the most part). The problem is I need to associate quantities and units with each Item.
Sample of UI I'm hoping for:
A big component of it is the ability to add/delete/edit the individual items. I imagine looking at the submitted form data, and using some jquery and clone would work. But I was wondering if someone already created a Behavior perhaps for this already?
Current Models (shortened to the relevant stuff, ie removed users/notes/etc):
class Item extends AppModel {
var $name = 'Item';
// id : int
// name : varchar
// unit_id : int
var $belongsTo = array(
'Unit' => array(
'className' => 'Unit',
'foreignKey' => 'unit_id'
),
);
var $hasAndBelongsToMany = array(
'Recipe' => array(
'className' => 'Recipe',
'joinTable' => 'recipes_items',
'foreignKey' => 'item_id',
'associationForeignKey' => 'recipe_id',
)
);
}
.
class Recipe extends AppModel {
var $name = 'recipe';
var $displayField = "name";
// id : int
// name : varchar
var $hasAndBelongsToMany = array(
'Item' => array(
'className' => 'Item',
'joinTable' => 'recipes_items',
'foreignKey' => 'recipe_id',
'associationForeignKey' => 'item_id',
)
);
}
.
class RecipesItem extends AppModel {
var $name = 'RecipesItem';
// id : int
// quantity : int
// unit_id : int
// recipe_id : int
// item_id : int
var $belongsTo = array(
'Unit' => array(
'className' => 'Unit',
'foreignKey' => 'unit_id'
),
'Recipe' => array(
'className' => 'Recipe',
'foreignKey' => 'recipe_id'
),
'Item' => array(
'className' => 'Item',
'foreignKey' => 'item_id'
)
);
}
Not quite sure what you're asking. For adding, editing and deleting items you would need create actions in your items controller. Saving association data (ie which Items a Recipe has) should be handled more-or-less automatically by the save() method in your controller action, assuming you have your forms set up correctly.
Out of curiosity, where did the RecipesItem model come from? What does that represent? If I am understanding you correctly, you have a Recipe model, and an Item model, with HABTM relationship. You shouldn't need a model for their join table, the recipes_items table just relates items from the two models.
that's not something Cake can do for you. Maybe there's some js that can helps you a bit, but you'll pretty much have to write your own javascript for that.
You have to use javascript to "transform" the select tag into something "cooler".
Here is the jquery-multiselect plugin which I use quite a bit. You can easily set it up to replace all of your multi selects with 1 line of code.
More info here:
http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/