How to extend entity classes with custom functions (business logic) in cakephp v3 - cakephp

In cakephp 3 (3.3.5, that is) I want to extend my entity classes with custom functions (business logic). For example:
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Something extends Entity {
public function isFoo() {
return true;
}
}
The corresponding table object looks like this:
namespace App\Model\Table;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\TableRegistry;
use App\Model\Entity\Something; // produces an `unused import' warning
class SomethingsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
...
}
...
}
In the controller, I use this code to retrieve the entity from the database and call the custom function:
class SomeOtherController extends AppController {
...
$this->loadModel('Somethings');
$thing = $this->SomethingsTable->get($id);
if ($thing->isFoo()) { ... }
...
}
However, this fails with a fatal error:
Error: Call to undefined method Cake\ORM\Entity::isFoo()
Note, when I do a
<?= var_dump($thing,true); ?>
in the corresponding view, $thing is shown as of type Cake\ORM\Entity.
How can I change the table's get() function to return entities with the correct type "Something" ?

It should be:
$thing = $this->Somethings->get($id);
// not
$thing = $this->SomethingsTable->get($id);
Thats why the Something entity is not used, but the default Entity class.
CakePHP autotables, since it can not find the SomethingsTableTable the default table class is used. Therefore also the default entity class is loaded.
If your test method would contain a query to the db, there would have been an error thrown, saying that somethings_table does not exist.

The problem is probably here:
class SomeOtherController extends AppController {
$this->loadModel('Somethings');
$thing = $this->SomethingsTable->get($id); // <-- Here
if ($thing->isFoo()) { ... }
}
Controller::loadModel does not set $this->SomethingsTable (which was probably set somewhere else in your code... ), but $this->Somethings, so this should be:
$this->loadModel('Somethings');
$thing = $this->Somethings->get($id);
if ($thing->isFoo()) { }
This code works, you do not need use App\Model\Entity\Something in SomethingsTable.php.
When trying to debug such thing, use debug() instead of var_dump:
Configure::write('debug', true); // If you are not already in debug mode
$this->loadModel('Somethings');
debug($this->Somethings);
Output:
object(App\Model\Table\SomethingsTable) {
'registryAlias' => 'Somethings',
'table' => 'somethings',
'alias' => 'Somethings',
'entityClass' => 'App\Model\Entity\Something', // Good!
'associations' => [],
'behaviors' => [],
'defaultConnection' => 'default',
'connectionName' => 'default'
}

This is an old post but I faced this issue today and the solution for me was slightly different. I was loading the model the right way, but my class name was not following naming conventions.
My Table: JobProfitsTable.php
My Entity: JobProfits.php (plural)
CakePhp is automatically looking for class named JobProfit.php (singular), and seems to fallback on Cake\ORM\Entity
So I had 2 options:
Rename my entity into JobProfit.php
Update my Table class with $this->setEntityClass('JobProfits')

Related

Cakephp 3 : How to insert data on db from a behavior

I've created a custom behavior on my CakePHP3 project and I would like to insert data on db from this behavior
Here is an clean exemple of my behavior, the function is well called and save of the form but the ADD Articles request doesn't work...
<?php
namespace App\Model\Behavior;
use Cake\ORM\Table;
use Cake\Event\Event;
use Cake\Core\Configure;
use Cake\I18n\I18n;
use Cake\ORM\Behavior;
use Cake\ORM\Entity;
use Cake\ORM\Query;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;
use App\Controller\AppController;
class HistorizeBehavior extends Behavior
{
public function beforeSave(Event $event, Entity $entity)
{
$this->historize($event, $entity);
}
public function historize(Event $event, Entity $entity) {
$articlesTable = TableRegistry::getTableLocator()->get('Articles');
$article = $articlesTable->newEntity();
$article->title = 'A New Article';
$article->body = 'This is the body of the article';
$articlesTable->save($article);
}
}
There is no error, no warning...but the data isn't saved..
Any idea why ?
Thanks

in cakephp4 how to access a model within a model

How do i access another model within a model in cakephp4.2? The docs on this issue isnt clear to me and i can then run a query on this ? TableRegistry is deprecated now.
error Unknown method "getTableLocator" called on App\Model\Table\LessonsTable
//none of these no longer work
in model {
use Cake\ORM\Locator\LocatorAwareTrait;
class LessonsTable extends Table
{
..
private function getlessonRevenue(){
//$clients = $this->getTableLocator()->get('Clients');
// $cleints = TableRegistry::get('Clients');
// $this->Table = TableRegistry::get('Clients');
$clients = $this->getTableLocator()->get('Clients');
https://api.cakephp.org/4.0/class-Cake.ORM.TableRegistry.html
Try:
<?php
use Cake\ORM\Locator\LocatorAwareTrait; //<------------ add here
class ArchivesTable extends Table
{
use LocatorAwareTrait; // <--------------------------- and add here
public function myMethod()
{
$clients = $this->getTableLocator()->get('Clients');
}
and read https://book.cakephp.org/4/en/orm/table-objects.html#using-the-tablelocator
and learn how to use php trait https://www.phptutorial.net/php-tutorial/php-traits/

Plugin cakephp-imagine-plugin Implementation Error

I am working with cakephp 3.6 and having trouble implementing a plugin (https://github.com/burzum/cakephp-imagine-plugin/)
It looks like it should be very simple to implement, but I can't figure it out and I'm probably missing something obvious.
Call to a member function processImage() on boolean
Per the installation instructions, I added the following code to config/bootstrap.php:
Plugin::load('Burzum/Imagine');
Configure::write('Imagine.salt', '$ntCdb-nQuImAnht3cC9PF4Q8P-bOXSppm^c3qcq');
I have inside src/Model/Table/PhotosTable.php:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class PhotosTable extends Table
{
public $name='Photo';
public $tablePrefix = '';
...
public function initialize(array $config)
{
$this->addBehavior('Burzum/Imagine.Imagine');
}
Finally, I added to my application's src/Controller/Controller.php the simple example function from the plugin's docs:
public function photos($id,$title)
{
$details=$this->Photos->photoDetails($id);
$imageOperations = array(
'thumbnail' => [
'height' => 450,
'width' => 450
],
);
$this->Image->processImage(
BASE_URL.'image/'.$details->photo_name,
BASE_URL.'image/thumb_'.$details->photo_name,
[],
$imageOperations
);
// ...

Cakephp 3 component and model has the same name

because my component, controller and model has the same name:
<?php
namespace Plug\Controller;
use Plug\Controller\AppController;
class SettingController extends AppController
{
public function initialize(){
parent::initialize();
$this->loadModel('Setting');
$this->loadComponent('Plug.Setting');
}
How do I know how to refer to component or model ?
Check the manual, almost everything is there. Please consider checking the documentation, it's there to be read.
Aliasing Components
One common setting to use is the className option, which allows you to alias components. This feature is useful when you want to replace $this->Auth or another common Component reference with a custom implementation:
// src/Controller/PostsController.php
class PostsController extends AppController
{
public function initialize()
{
$this->loadComponent('Auth', [
'className' => 'MyAuth'
]);
}
}
// src/Controller/Component/MyAuthComponent.php
use Cake\Controller\Component\AuthComponent;
class MyAuthComponent extends AuthComponent
{
// Add your code to override the core AuthComponent
}

Cakephp 3 callbacks, behaviors for all models

I just started reading cakephp 3 docs (I have been developing with cake 2.x for some time) and want to migrate some website from 2.x to 3. In cake 2 in my AppModel I have some callbacks, particularly beforeFind and beforeSave, that contain some logic concerning almost all tables in a database.
Now in cake 3 there is no AppModel, how do I get the same thing done ? The best I can think of is to put that code in some behavior's callbacks, but I have like 30 models, should I load the behavior in all models one by one ?
Thanks
You can also create an AppTable in your src/Model/Table Folder:
namespace App\Model\Table;
use Cake\ORM\Table;
class AppTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
$this->addBehavior('myBehavior');
$this->addBehavior('myBehavior2');
$this->addBehavior('myBehavior3');
}
}
And then extends your Table class by AppTable:
namespace App\Model\Table;
use App\Model\Table\AppTable;
class ArticlesTable extends AppTable
{
}
Use an event listener that listens to the events Model.beforeSave, Model.beforeFind and Model.initialize and apply whatever you want to do there. Read the chapter about events and the documentation for table callbacks.
use Cake\Event\EventListenerInterface;
use Cake\Event\Event;
class SomeListener implements EventListenerInterface
{
public function implementedEvents()
{
return [
'Model.beforeFind' => 'beforeFind',
];
}
public function beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)
{
// Your code here
}
}
And attach it to the global event manager. It will now listen to the callbacks of all table object.

Resources