CakePHP 4 - Unknown method "loadModel" - cakephp

In a CakePHP 4 application I've added this to a model, src/Model/Table/SignUpsTable.php
public function initialize(array $config): void
{
parent::initialize($config);
// ...
$this->loadModel('EmailDomains');
}
I have a model called EmailDomains and the appropriate Table/Entity class files exist.
In a controller method I have the following:
// src/Controller/UsersController.php
public function initialize(): void
{
parent::initialize();
$this->loadModel('SignUps');
}
public function signUp()
{
// ...
}
When navigating to /sign-up in my browser it gives the following error:
Unknown method "loadModel" called on App\Model\Table\SignUpsTable
If I comment-out the loadModel code in SignUpsTable::initialize it clears this error but then other code I have in there won't work as it references $this->EmailDomains->...

That's because loadModel is in fact not a method in that class.
\TableRegistry::getTableLocator()->get('EmailDomains') will return the table object (which you can reference directly if you just need it once, or save in a local variable or class property)
Or if you use Cake\Datasource\ModelAwareTrait; in your particular table class, then the loadModel function should become available to you.

Related

How can I call custom function from Model?

I'm using CakePHP3.0.5 and I'm just calling custom function from Model.
But it shows Unknown method error.
How can I call custom function from controller?
// Controller
class TopController extends AppController {
public function initialize() {
parent::initialize();
$this->loadModel('TopRawSql');
}
public function showTop(){
$data = TableRegistry::get('TopRawSql');
$data->getTest(); // Unknown method error occur
}
}
// Model
class TopRawSql extends Table {
public function getTest() {
return 'OK';
}
}
I tracked the error message and found following the code.
Does it mean, can't I use custom function name without 'find' prefix?
// vendor->cakephp->src->ORM->Table.php
public function __call($method, $args)
{
if ($this->_behaviors && $this->_behaviors->hasMethod($method)) {
return $this->_behaviors->call($method, $args);
}
if (preg_match('/^find(?:\w+)?By/', $method) > 0) {
return $this->_dynamicFinder($method, $args);
}
throw new \BadMethodCallException(
sprintf('Unknown method "%s"', $method)
);
}
Your table class name is wrong, all table classes must end in Table, ie TopRawSqlTable.
Given the wrong class name, I assume that the filename might be wrong too. And I don't know if you have the correct namespace set in the file, or any namespace at all since it's not shown in your question, but that needs to adhere to the conventions accordingly too, and would usually be App\Model\Table.
If the class for the requested alias cannot be found, then a generic instance of \Cake\ORM\Table will be used, which will then of course cause a failure as it won't have any of your custom code.
See also
Cookbook > CakePHP at a Glance > CakePHP Conventions > Model Conventions
Cookbook > Database Access & ORM > Table Objects
Cookbook > Configuration > Disabling Generic Tables
To answer your question, yes. The call() magic function is going to require every function within the body of a Model to be either a get* or set* function. But the code you quoted points you directly to the answer you're looking for: create a Behavior and call the function whatever you'd like.

Cakephp 3 : How loadModel function defines globally?

I am new to cakephp3.I read http://book.cakephp.org/3.0/ and start developing small application using cakephp3.
I am using UserMastersController and I want to save and get data from person_masters table.For that each time I am using loadModel() or TabelRegistry::get(); methods in each function.
$this->loadModel('PersonMasters');
So, how can I define globally other models in cakephp3, so I should not define each time in each function ?
use the beforeFilter
I am using loadModel() or TabelRegistry::get(); methods in each function.
The simplest solution is to define the beforeFilter method in the relevant controller:
public function beforeFilter(Event $event)
{
$this->loadModel('PersonMasters');
parent::beforeFilter($event);
}
public function example()
{
$this->PersonMasters->find(); // no error
}
If you prefer, you could instead change the name of the default table class, which is set automatically to the name of the controller class - if it isn't already set. This is how the convention of "FoosController will use the Foos table class" is implemented. I.e. this would also work:
public function beforeFilter(Event $event)
{
$this->modelClass = 'PersonMasters';
parent::beforeFilter($event);
}
public function example()
{
$this->PersonMasters->find(); // no error
}
Though that's more indirect and probably more confusing for future readers of the code (e.g. you, next year, next week - or tomorrow =)).

how to use common function in helper and component In Cakephp

We are familiar with Components and Helpers in CakePHP.
I have an ABC Component and XYZ helper and both have same function (around 2000 lines total 4000 lines).
there is any way to use same function in Controller and .CTP files. it's not good to use same function 2 time.
any method so i can use Component/Helper function in Helper/Component without rewrite ?
same method into component and helper >>
Component
class DATAComponent extends Component {
public $components = array('Session', 'THmail');
public function UsaStateList()
{ /********/}
Helper
class LabHelper extends AppHelper {
public function UsaStateList()
{ /********/}
}
Well, you will have to rewrite something, it's not going to solve itself.
CakePHP is still PHP, so you can easily apply common patterns to keeps things DRY. The most straight forward way would probably be to move the shared functionality into an utility class that your component and helper can both use internally while leaving their public API unchanged.
Some of CakePHPs helpers do something similar, check for example the time helper.
http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html
http://book.cakephp.org/2.0/en/core-utility-libraries/time.html#CakeTime
Traits might be an option too, depending on the amount of functionality being shared and how much it is tied to the use in a component/helper.
I wanted to use a component inside a helper. So i came out with the following solution.
<?php
App::uses('AppHelper', 'View/Helper');
App::import('Component', 'MyComponent');
class MyHelperHelper extends AppHelper {
private $MyComponent = null;
public function __construct(View $View, $settings = array()) {
$collection = new ComponentCollection();
$this->MyComponent = new MyComponentComponent($collection);
parent::__construct($View, $settings);
}
public function myCustomFunction() {
return $this->MyComponent->myCustomFunction();
}
}
Simple Answer
For common functions across your application, add a Lib or Utility class.
app/Lib/MyClass.php
class MyClass {
public static function usaStateList() {
// ...
}
}
Then add this at the top of whichever file you want access to the function:
App::uses('MyClass', 'Lib');
And call your function wherever you like:
$myClass = new MyClass();
$states = $myClass::usaStateList();
Better Answer
It looks to me like your specific problem is that you want to be able to get a list of US states in both your controller and your view. The best way to do this is to have a database table of US states.
Create a table in your database called us_states.
Example SQL:
CREATE TABLE `us_states` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(20) NOT NULL,
`abbreviation` CHAR(2) NOT NULL
) ENGINE = MYISAM
Insert all the states as data in that table. You can find SQL dumps on the Internet which already have that for you (e.g. Like this one).
Create a UsState model in CakePHP.
/**
* Model for US States
*
* #package app.Model
*/
class UsState extends AppModel {
/**
* Default sort order
*
* #var string|array
*/
public $order = 'UsState.name';
}
What you can then do is access the states from your controller just by using the model.
/**
* Your Controller
*
* #package app.Controller
*/
class YourController extends AppController {
public function index() {
// Get a list of US states
$this->loadModel('UsState');
$states = $this->UsState->find('all');
}
}
And if you want to access those states in your View, then you should pass along that data as you normally would any other variables.
I imagine you would want to do that so you can have a select menu of US states, perhaps.
public function index() {
// Get a list of US states
$this->loadModel('UsState');
$states = $this->UsState->find('all');
// Make the states into an array we can use for a select menu
$stateOptions = array();
foreach ($states as $state) {
$stateOptions[$state['id']] = $state['name'];
}
// Send the options to the View
$this->set(compact('stateOptions'));
}
And in your view you can display a select menu for that like this:
echo $this->Form->select('us_state_id', $stateOptions);
I would go with a class in Lib folder. It is pretty clear how to deal with a library class that has only static methods. So, I omit this case. A workable solution for instantiating the class could be to create it in the controller and put it into the registry. If you really need to access CakeRequest, CakeResponse and CakeSession (take a note that CakeSession has many static methods, so you often do not need an instance of that class) from that class you can set it from the controller:
$MyLib = new MyLib();
$MyLib->setRequest($this->request); // optional
ClassRegistry::addObject('MyLib', $MyLib);
Then from the view or any other place you would just get an instance of MyLib from the registry:
ClassRegistry::getObject('MyLib');
or simply
$list = ClassRegistry::getObject('MyLib')->UsaStateList();
So, your class would be something like this:
// /Lib/MyLib.php
class MyLib {
public function setRequest(CakeRequest request) {...}
public function UsaStateList() {...}
}
ok you want to use a single function in component and helper, I can think of 3 things you can do:
Calling a function from the component in your helper.
Calling a function from a helper in your component.
Create a model or use an existing model where you put the function, and you can use this function in your component or your help.
Option numbre 3:
In your helper and component, you need import a model, assuming that your function be in a model "StateList":
how you call the funcion of the model "StateList" in your helper, so:
App::import("Model", "StateList");
$model = new StateList();
$model->UsaStateList();
how you call the funcion of the model "StateList" in your component, so:
$model = ClassRegistry::init('StateList');
$model->UsaStateList();
ans if you want use the same function in a controller just:
$this->loadModel('StateList');
$this->StateList->UsaStateList();

Symfony 2 FatalErrorException: Error: Call to a member function has() on a non-object

Symfony 2 typical problem, yet no clear response to it(I did some research).
Given the following "DefaultController" class which actually works:
<?php
namespace obbex\AdsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
public function indexAction()
{
$em = $this->getDoctrine()->getEntityManager();
$connection=$em->getConnection();
$string="SELECT DISTINCT country_code FROM country_data";
$statement = $connection->prepare($string);
$statement->execute();
$result = $statement->fetchAll();
var_dump($result); //works not problem
die();
}
}
I want to delegate database calls to another class called "DatabaseController", the "DefaultController" now is set as following:
<?php
namespace obbex\AdsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use obbex\AdsBundle\Controller\DatabaseController; //new DatabaseController
class DefaultController extends Controller
{
public function indexAction()
{
$dbController = new DatabaseController();
$res = $dbController->getQuery();
}
}
and the "DatabaseController" is set as following:
namespace obbex\AdsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DatabaseController extends Controller{
public function __construct() {
}
public function getQuery()
{
$em = $this->getDoctrine()->getEntityManager();
$connection=$em->getConnection();
$string="SELECT DISTINCT country_code FROM country_data";
$statement = $connection->prepare($string);
$statement->execute();
return $statement->fetchAll();
}
}
And this throw and the following error: FatalErrorException: Error: Call to a member function has() on a non-object in /home/alfonso/sites/ads.obbex.com/public_html/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php line 202
my mind is blowing right now because I am extending the exact same class "Controller". Why it is working in one case and not in the other?
Apparently it is a "container problem" that can be set trough a service according to a response in another thread or via extending the "Controller· class however does not work in this case.
First of all, you shouldn`t delegate database management to another controller, that's a bad practice.
Instead, you can inject a service containing all DB logic
Symfony2 Use Doctrine in Service Container
Or use a EntityRepository
http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
Regarding the issue with has() function, you are creating an instance of a Controller without any container on it. Therefore, when the controller tries to call $this->container->has() throws an error, as container is not defined.
I finally set the object caller and I requested the container service as follows:
on the services.yml file
service:
manage_ads:
class: obbex\AdsBundle\Classes\ManageAds
calls:
- [setContainer, ["#service_container"]]
on the main controller:
$ads_manager = $this->get('manage_ads');
$ads_manager->functionCallingTheRawQuery();
But I still use this optionally because now I am setting my queries from the repository of an entity rather than create my own objects (For now, I am new to symfony2)

CakePHP reference this controller with $this

Using CakePHP how can you get a reference to this controller without specifying it's name?
Why would you ever want to reference a controller by name? Cake only ever has one controller loaded, you should not be loading other controllers in a single request. 1 request == 1 controller.
If you want to referent the model without the name, the default model name is in $this->modelClass
$this->{$this->modelClass}->method();
The accepted answer (if it is a model you want) is wrong.
class UsersController extends AppController {
public $uses = array('People');
public function method() {
$this->{$this->modelClass}->method(); // works
$controller = Inflector::singularize($this->name);
$this->$controller->someMethod(); // fatal error
}
}
I thought I needed to reference this controller to create the following public function.
But the dogmatic69 way is much nicer.
// Controller/AppController.php
public function dumpData() {
if(!Configure::read('debug') > 2)
throw new Exception(".dumpData() can only be accessed in debug mode.");
// $controller = Inflector::singularize($this->name);
// $data = $this->$controller->find('first');
$this->{$this->modelClass}->find('first');
die(debug($data));
}

Resources