Associate/Dissociate related entity on (new) entity - cakephp

Is there a way to associate/dissociate one entity to another in CakePHP4.x?
Similar to Laravel's? https://laravel.com/docs/8.x/eloquent-relationships#updating-belongs-to-relationships
For instance, if i create a new entity and assign a related entity like this:
#in a controller
$entity = $this->Entity->newEmptyEntity();
$related = $this->Related->get(1);
$entity->set('related', $related);
This will bind $related to $entity->related but it wont set $entity->relation_id = 1.
I suspect that $this->Entity->save($entity) will set $entity->relation_id, but i don't want to save it.
One way to fix it would be:
$entity->set(['related_id' => $related->id ,'related', $related]);
That doesn't look very elegant?

There is no equivalent shorthand method for that in CakePHP.
While belongsToMany and hasMany associations have the link() and unlink() methods to associate and save entities, there is nothing similar (yet) for belongsTo or hasOne.
So for now you'd have to manually set the entity on the correct property, and then save the source entity, for example:
$entity = $this->Table->newEmptyEntity(); // or $this->Table->get(1); to update
$entity->set('related', $this->Related->get(1));
$this->Table->save($entity);
After saving, the source entity will hold the foreign key(s) of the newly associated record. If you do not actually want to save it (for whatever reason), then you have no choice but to manually set the foreign key(s) on the entity, or to implement your own helper method that is aware of the association configuration, so that it would know which properties to populate.
Just to get you started with something, in a custom \Cake\ORM\Association\BelongsTo based association class this could look something like this:
public function associate(EntityInterface $source, EntityInterface $target)
{
$source->set($this->getProperty(), $target);
$foreignKeys = (array)$this->getForeignKey();
$bindingKeys = (array)$this->getBindingKey();
foreach ($foreignKeys as $index => $foreignKey) {
$source->set($foreignKey, $target->get($bindingKeys[$index]));
}
}
and could then be used like:
$entity = $this->Table->newEmptyEntity();
$this->Table->Related->associate($entity, $this->Related->get(1));

Related

CakePHP Access Multiple Database Table from a Model

I am using CakePHP 2.x
There are several database tables setup :
1) Fruit
2) Vege
3) Drink
I am able to access these database tables in a CONTROLLER using this line below. With this line, I am able to access these other tables.
public $uses = array('Get', 'Fruit', 'Vege', 'Drink');
My problem is when trying to access them in a MODEL. When I try this code below, an error occurs.
App::uses('AppModel', 'Model');
class Get extends AppModel {
public function getHistory( $limit ) {
$searchLimit = $limit;
$raw = $this->Fruit->find('all')
An error occurs at the line '$this->Fruit'.
Call to a member function find() on a non-object...
Any ideas how to call multiple database tables in a single MODEL ?
As accessing all the database tables work perfectly in the CONTROLLER, I did this in the CONTROLLER instead of the MODEL. It seems much easier and straight forward to do this in CONTROLLER.
A private function is created in the CONTROLLER, and accessed by other functions using '$this->myPrivateFunctionName()'
First you should read on model associations Model associations
Second, if that doesn't fit your needs (meaning there is no clear association between models, honestly I don't see connection between Get and Fruit) you can use ClassRegistry:
$Fruit = ClassRegistry::init('Fruit');
Before you can use ClassRegistry you must add this before class definition:
App::uses('ClassRegistry', 'Utility');
Associate model by $belongsTo, $hasMany or what ever relation you want.
than access by $this->Fruit->find()

Getting CakePHP to work with existing "non-Cake" database

I am building a new app based on multiple databases with many tables which don't follow any Cake conventions. I want to use CakePHP but I'm not sure if it's possible without the database in a Cake format.
Problems include:
Tables not named as Cake expects
Primary keys are not necessarily named id (e.g. it might be order_id)
Foreign keys are not necessarily named like other_table_id
Changing the database tables is not an option.
Is it possible to manually configure the schema in each model so that Cake will then know how the model relationships need to work? Or should I just give up on using Cake?
yes. you can still use CakePHP in your case.
Check out various Model attributes to fit your needs
http://book.cakephp.org/2.0/en/models/model-attributes.html.
e.g.
public $useTable = 'exmp' can be used to configure what table to use.
public $primaryKey = 'example_id'; can be used to configure the primary key's name
**Try this code sample............**
More detail here http://book.cakephp.org/2.0/en/models/model-attributes.html
<?php
class Example extends AppModel {
// Name of the model. If you do not specify it in your model file it will be set to the class name by constructor.
public $name = 'Example';
// table name example
public $useTable = example;
// example_id is the field name in the database
public $primaryKey = 'example_id';
}
?>

Calling a model method from the controller in CakePHP

Is there no way to access a Model instance as an object (as opposed to as an array) within a Model method in CakePHP? As a super-simplified example, my instinct tells me it ought to be possible to do something like this:
Bird.php
...
public function specialName()
{
$name = $this->name;
return "Oh wow! It's ".$name;
}
If I call this method from my BirdController.php like so:
public function view($id) {
if (!$id) {
throw new NotFoundException(__('Invalid bird'));
}
$this->Bird->id = $id;
$this->set('results', $this->Bird->specialName());
}
... then it displays in the view file as "Oh wow! It's Bird" rather than "Oh wow! It's Freddy" (i.e. the name of the model, not the name of the model instance).
I've tried variations on this general approach, to no avail. It seems I must access the information via an array, like so:
Bird.php
...
public function specialName($id)
{
$data = $this->findById($id);
$name = $data['Bird']['name'];
return "Oh wow! It's ".$name;
}
This seems WAY over-complicated to me. What am I missing? Ultimately I want to be able to access dependent models in my model function, e.g. get all of the associated Bird->Subspecies. It seems like this would be much easier to do working with objects.
If you would have read the documentation about models you would know that Model::$name is the name of the model. There is also Model::alias which is the name of the model when it is accessed trough associations and the association is named different than the model name.
Cakes ORM does not return results as data objects, 3.0 will do that. Most easy way to return a specifc field right now is to do this in a model:
public function specialName($id) {
$this->id = $id;
return $this->field('name');
}
Also the "Oh wow..." should go to the view where you would echo the name that you set as $result.
If you want data objects, I know there is a plugin, a behaviour that will turn the result sets into data objects. Just google it.
Ultimately I want to be able to access dependent models in my model
function, e.g. get all of the associated Bird->Subspecies.
Did you read the book? See this section. With containable you can control what exactly you want to fetch from the associated models.
I recommend you to do the blog tutorial first to get an idea of the basics of how CakePHP works.

CakePHP: To Create A New Controller

I'm using CakePHP 2.0.5 (but this isn't necessarily a cakephp specific question). I have a Coupon and a User model. Each time a user prints a coupon (proccessed by: Coupon Controller):
class CouponsController extends AppController {
public function printcoupon($id = null) {
// code
}
}
I want to save the information to a "coupons_printed" table (id/coupon_id/user_id/created). Should I create a new model for this, or should I just create a function inside of the Coupon model similar to (and call it in the controller each time that page is viewed)?:
class Coupon extends AppModel {
function insertIntoPrinted($id) {
$this->query("UPDATE coupons_printed SET .....");
}
}
Whatever you do, a raw SQL query is not the best way to go. Always use CakePHP methods if at all possible (and almost always it is possible).
You should put the insertIntoPrinted() function in the CouponsPrinted model (although, as a side note, PrintedCoupon would be a more natural way to name the model...) You can then add a HasMany relationship to the Coupon model ($hasMany = array( 'CouponsPrinted' )) and call the function in the CouponsController:
public function printcoupon($id = null) {
$this->Coupon->CouponsPrinted->insertIntoPrinted( $id );
}
CakePHP's model has a thing call association.
In your case, Coupon has a hasMany association with coupons_printed.
You can create a new model, or query using the association in the Coupon model, the generated queries will be the same, I believe.
Your CouponsController already depend on Coupon Model, so not creating another model is a better solution.

Problem with altering model attributes in controller

Today I've got a problem when I tried using following code to alter the model attribute in the controller
function userlist($trigger = 1)
{
if($trigger == 1)
{
$this->User->useTable = 'betausers'; //'betausers' is completely the same structure as table 'users'
}
$users = $this->User->find('all');
debug($users);
}
And the model file is
class User extends AppModel
{
var $name = "User";
//var $useTable = 'betausers';
function beforeFind() //only for debug
{
debug($this->useTable);
}
}
The debug message in the model showed the userTable attribute had been changed to betausers.And It was supposed to show all records in table betausers.However,I still got the data in the users,which quite confused me.And I hope someone can show me some directions to solve this problem.
Regards
Model::useTable is only consulted during model instantiation (see the API documentation for Model::__construct). If you want to change the model's table on the fly, you should use Model::setSource:
if ( $trigger == 1 ) {
$this->User->setSource('betausers');
}
The table to use is "fixed" when the model is loaded/instantiated. At that time a DB connection object is created, the table schema is being checked and a lot of other things happen. You can change that variable later all you want, Cake is not looking at it anymore after that point.
One model should be associated with one table, and that association shouldn't change during runtime. You'll need to make another model BetaUser and dynamically change the model you're using. Or rethink your database schema, a simple flag to distinguish beta users from regular users within the users table may be better than a whole new table.

Resources