I have 2 controllers: UsersController and AnalyticsController.
When I run:
//UsersController:
function dummyFunction(){
$this->Analytic->_loadChartFromId($chart_id);
}
the output is:
Query: _loadChartFromId
Warning (512): SQL Error: 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '_loadChartFromId' at line 1 [CORE\cake\libs\model\datasources\dbo_source.php, line 684]
The _loadChartFromId() function takes $chart_id as an argument and returns an array as output. I have no idea why Query: _loadChartFromId appears.
You don't call other controller methods from your controller.
In your users controller, $this->Analytic is an instance of the Analytic model, not the AnalyticsController. So CakePHP thinks you are trying to call a public method called _loadChartFromId() on the Analytic model, which, as you know, doesn't exist.
The reason you get the error is because if you try to call a non-existent method of a model, CakePHP tries to convert it to one of its Magic Find Types. Of course, it's not a valid Magic Find Type either, so you get a SQL error.
Solution
It's difficult to provide a complete solution as we only have part of your code, but you are perhaps violating the concept of MVC with the way you're coding your app.
You need to do one of two things:
Move _loadChartFromId() to your users controller. This seems to me like it would be counter-intuitive, as it probably has nothing to do with the User.
Move the method to your Analytic model. You would need to make it public so the controller can access it, and in your users controller you would need to make sure you have the Analytic model loaded.
class Analytic extends AppModel {
public function _loadChartFromId($chart_id) {
// ...
}
}
Then you can call the method as you were doing before, from your users controller.
I could have opted to close this question as an exact duplicate of at least 5 other questions (if you search for "cakephp another controller").
But the answers there are just terrible. They actually try to invoke new Dispatchers or requestAction().
So if your question is about another controller method:
The short answer is: You don't.
The long answer: You still dont. That's a typical beginners mistake.
You should put the functionality into a component if it is mainly business logic. The component then can be accessed from multiple controllers.
If it is more like model data (as in your example), put the functionality into the model layer (in an appropriate model). this way you can also access it from anywhere in your application.
Also: Accessing protected methods from other objects is never a good idea. Use public methods if you intend to use it from "outside" the object.
If your question is about a model method:
You need to include your model in your controller before you can use it.
Either by using public $uses or by using loadModel('ModelName') or even ClassRegistry::init('ModelName').
Related
Concise: How I can avoid using static methods in a model?
Loquacious: Suppose I have a user class. Having userID I can get user name by (new user($userID))->getUserName(). Fine, what if I want to lookup a user? (new user())->lookup($uname, $pass). Still fine, but the latter case could be done via a simple static method user::lookup($uname, $pass)!
Some thoughts:
It's OK! Use (new object())->method() whenever you want. So should I create a hollow object to call a function?
Move this function out of your model. If it needs a DB lookup, where is better than Model context?
Define it as a static method. So isn't it lame to have a mixture of public and static methods in a class?
Side note: I've searched this question, no avail!
Move this function out of your model. If it needs a DB lookup, where is better than Model context?
Yes, indeed, this is the best way to solve the problem.
Currently your User class violates single responsibility principle which basically, says "one task - one class".
Right now your User describes user entity/state and handles persistence (in your case - retrieval from database). See, two things.
I suggest you create another class that is going to handle persistence tasks, like add/update/delete user. The simplest solution is to create a primitive repostitory, like this:
<?php
class UserRepository
{
public function addUser(User $user);
public function updateUser(User $user);
public function deleteUser(User $user);
public function getUserById($id);
}
Then retrieval of user can be done in the following manner:
// get an instance of this repository class
$userRepository = new UserRepository;
// ask it to find and return user from the database by ID
$user = $userRepository->getUserById($_GET['id']);
Easy to read, easy to handle, right?
This UserRepository class is actually a primitive implementation of Repository Pattern. UserRepository emulates an in-memory collection of all of your users, hiding implementation inside. It hides actual persistence mechanism from you as user: imagine, your coleague would write this class and you're just using its methods, like UserRepository::getById(1) - you don't even know/care if it grabs data from files/db/API. That's neat. )
This particular implementation is described very clearly in Kristopher Wilson's book "The Clean Architecture in PHP", which I highly recommed for you to read: it will take you two-three evenings, and push you to the next level.
You can extend the list of methods, of course, add lookups, etc.
class UserRepository
{
public function getByCompany(Company $company);
public function getByEmail($email);
public function countTotal();
}
In fact, every time you need to grab/add/update user in the database, you should do it via this repository.
I would like to emphasize that this is a simple implementation of the pattern, particularly, if you compare it to what Martin Fowler describes as Repository. However, in most cases it's totally fine.
It's OK! Use (new object())->method() whenever you want. So should I create a hollow object to call a function?
depends on how much creating an instance will cost
Move this function out of your model
Factory Pattern comes in mind here.
notes here:
What happens when $userID in the first call do not exists?
Isnt your lookup() method not creating 2 instances at one call, first for lookup, second the found one that is returned?
A FactoryPattern for example can have findByID() or findByName() and return an UserObject. And all that should not depend on this syntax at all: (new object())->method(), that is nice, but not always best practise.
I'm updating a PHP framework I've written. It used to just use a default behavior for routing. For example consider a case where the request goes to domain.com/package/controller/method...
$url = ["package", "controller", "method"];
//Check if package exists...
//Check if controller exists in package...
//Check if method exists in controller...
This is all well and good, and works perfectly. However, I wanted to add some additional functionality to my router. That functionality being the ability to define custom routes, and pass an anonymous function which does whatever you want.
However, supposing that the request does not match any of the user-defined routes, I want to use the default functionality I have now to check if there are additional possible routes. That way I can update old projects with the new framework and not have them break, and additionally...I just like this default behavior because most of the time routes are not that complicated and defining routes feels like a violation of DRY to me.
The problem is that I don't want to pass the user-defined routes as an array to the object constructor. Rather, I want the user to call them as methods on the base application object similar to how laravel or express handles this.
The problem is that I want the default route checking to happen AFTER the user's defined routes have been checked not before. This quasi-code might help you understand what I mean...
class App
{
__construct
{
//Check Default Routing
}
private function get()
{
//Get Request
}
private function post()
{
//Post Request
}
private function put()
{
//Put Request
}
private function delete()
{
//Delete Request
}
}
app::get();
In the above case, the default routing would take place before the user-defined routes are called. I looked at the PHP consrtuctor/destructor page and learned about __destruct. However, after reading this question I'm a little bit unsure this would work.
PHP.net says...
The destructor method will be called as soon as there are no other
references to a particular object, or in any order during the shutdown
sequence.
The first part of that explanation sounds like exactly what I want. I.E. as soon as all of the methods have been called on the application object, we'll run the __destruct function which will check if the user-defined routes were fruitful, and if not, check if the default routing system yields any results.
The problem is that I'm not sure if this is bad practice, or simply won't work. Can I require a file, set my controller, and then call a method on that controller from within __destruct? Are there limitations that would effect the code within these controllers? Supposing that there is a problem using __destruct this way, what are my alternatives, keeping in mind I don't like either of these solutions...
Having the user call the default routing as a method at the end of their script.
Passing routes in as arrays to the constructor.
I think you're confused here. Take note of this from the PHP Manual
The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence.
To put this a different way, there's two reasons to call a destructor
The class is being garbage collected. In other words, you've overwritten or unset all the references to the class instance. This means you can't directly call this class anymore
The PHP script has reached its end and the thread is shutting down.
In other words, there's nothing left for the class to do. But in your own statement you say this
The first part of that explanation sounds like exactly what I want. I.E. as soon as all of the methods have been called on the application object, we'll run the __destruct function which will check if the user-defined routes were fruitful, and if not, check if the default routing system yields any results.
There is no "and" here to work with. That's the point. In fact, there's very few places you would use this.
What you need is to think in layers. So you'd have a controller layer that you'd call to check the methods. In turn, that controller opens a new layer that checks user functions. That class or method should return something or throw an Exception if it fails. On failure it can then try to use default methods. This is how you need to structure your program. Trying to use a destructor to do this would likely only confuse people. Make the data flow explicit, not implicit (which is what magic methods do).
I have been wondering for a while why this works. I have been working with Laravel 5.1 for a while and now I want to create my own framework (for learning purpose only). At the moment I am into Models and Database connections, and then there is this file. Which is extended by Models, such as Status and User models.
https://github.com/illuminate/database/blob/master/Eloquent/Model.php
This is the abstract Model class which isn't too bad. When setting up a Laravel 5.1 project there will be a User class that extends this, it is the possible to execute
User::where('username', 'Rasmus');
User is the user model that extends the abstract Model class
'username' refers to the the database column
'Rasmus' is what it is looking for in that column, it is my name...
The interesting part and the main thing in this question is that there is no "where" method in the Model class, nor the User class. The Model class does NOT inherit anything. Can someone explain to me why this works, (calling User::where) when the method does not exist.
Good to know is that static::where(...); is called a few times in the abstract Model class as well.
Sincerely, Rasmus Rosengren
A fellow PHP learner
Model's __call() function passes it off to a QueryBuilder instance (with a few steps in between, but that's the fundamental end result).
public function __call($method, $parameters)
{
...
$query = $this->newQuery();
return call_user_func_array([$query, $method], $parameters);
}
__call() is a magic method that lets the application deal with a missing/inaccessible method name. In this case, it allows the various where() methods (including being able to do stuff like definitely undefined functions like whereUsername('Rasmus')) to work.
I'm not programming expert and i do expect better answer on this. However, you might be interested in QueryBuilder at line 447. While on Model.php you could see it uses QueryBuilder on line 27.
As i remember Laravel uses trait for handling several code reuse. It is rather advanced topic - which pretty interesting.
I am trying to upgrade my CakePHP application from 1.3.6 to 2.2.4
I did all the upgrade steps based on official CakePHP upgrade documentation.
But i am struggling with this error:
Class 'Content' not found in
C:\wamp\www\cakephp-2.2.4\app\Controller\Component\OrderBaseComponent.php
on line 20
Argument 1 passed to Component::__construct() must be an instance of
ComponentCollection, none given, called in
C:\wamp\www\cakephp-2.2.4\app\Controller\Component\OrderBaseComponent.php
on line 17 and defined [CORE\Cake\Controller\Component.php, line 77]
For the first error make sure you always App::uses() every single class you use in your code.
So your Content (whatever class it is) must also be included before you can actually use it.
Only if it is a model you can just use ClassRegistry::init(), otherwise put sth like
App::uses('Content', '[TYPE]'); at the top of the file.
This second error is pretty self-explanatory!
Look into the outlined "CORE\Cake\Controller\Component.php" file and make sure your function does have the exact same arguments in your custom component:
public function __construct(ComponentCollection $collection, $settings = array()) {
//...
}
We don't have any relevant source code but generally you might add this at the top or your component file:
First:
App::uses('Content', 'Model');
And you might have to add something like in your init or construct method:
$this->Content = ClassRegistry::init('Content');
That should solve the first issue or give a clear explanation about what goes wrong. Actually this code was likely already not correctly independent functional.
I suspect it depends on the Model to be loaded already in another piece of code that's why it worked likely. A component should work not dependent on other pieces of code so adding the App::uses, App::import etc statements makes the code work always. For example when you re-use it in your other projects.
Second:
The second issue comes really with migration issues. Check that the model extends the Component class first.
Then make sure that if you implement a custom method __construct() but also init() for example that you check whether you should add a call to the parent. For example this applies to beforeFilter controller method.
public function beforeFilter() {
parent::beforeFilter();
}
Documentation and code example from: http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
Relevant piece of documentation: http://book.cakephp.org/2.0/en/appendices/2-0-migration-guide.html#components
Here is a quick overview of the controllers functionality in most of the application:
controller loads a specific model, gets data from it, formats the data and passes the formatted data to the view.
Now there is a search page, which needs to do a search query over entire database (all models). It needs to show each type of data in its particular formatted output on a single page as a list.
The problem:
The search controller can do the search, dynamically load model for each record type, and get the data from model. Problem comes when the data needs to be formatted. I am trying to load the specific controller from the search controller, which is causing problems.
What to do?
PS: I tried using the 'Wick' library, but it fails when the controller's format function tries to use its own model and session object, giving errors about call to a member on a non-object.
After much refactoring and trial/error, It appears that the best way to achieve the above is this way:
Keep the format function in the base controller from which all other controllers are derived. The format options are passed to the function along with the data object as arguments.
Make a static function in each derived controller, which returns the formatting options of the data.
Inside the search controller (which is itself derived from the base controller), for each data object, call the static function of its particular controller which returns the data formatting options, then use that to format the object for the view.
I guess I can say I will stick to using the model only for interaction with the database, and let everything else be done by controller. If anyone has a better solution still, I am all ears.
It sounds like you want to use the Factory design pattern
Make this a library:
class MyModelFactory {
static public function Factory($data) {
$type = key($data);
return new $type($data);
}
}
now, in your controller, you can do something like this:
$model = MyModelFactory::Factory(array($_REQUEST['model'] => $_REQUEST));
and now you have an object of whatever model was specified in $_REQUEST['model']. Be sure to take any security precautions you may need for your application to assure the user has permissions to use the model that they request
Now, since you want to be using common methods and stuff, your models should probably be based off an abstract class / interface.. so instead of
class MyModelOne extends Model {
// stuff
}
You probably want something like this, to ensure your required methods will always be available:
abstract class MyAbstractModel extends Model {
protected $search_params;
public function __construct($data = array()) {
$search_params = $data['search_params'];
}
protected function GetSearchParameters() {
return $this->search_params;
}
abstract public function GetData();
abstract public function GetColumns();
abstract public function DefineViewOptions();
}
class MyModelOne extends MyAbstractModel {
public function GetData() {
$params = array();
$params[] = $this->db->escape_str($this->GetSearchParameters());
// return whatever data you want, given the search parameter(s)
}
public function GetColumns() {
// return some columns
}
public function DefineViewOptions() {
// return some configuration options
}
}
In general you can't load another controller from within a controller in CodeIgniter (although there are mods that allow you to do something like this).
I would try creating a class for formatting your data and add it to the application/library folder. Then load, use and re-use this class throughout your various controllers.
Here is a page from the CodeIgniter documentation Creating Your Own Libraries that explains the details and conventions.
Also, if a class is overkill, creating helper functions is an even lighter approach.
The difference between libraries and helpers in CodeIgniter is that libraries are classes, helpers are just a group of php functions.
Once you have formatted your data, you can load any view from any controller, so you should still have all the re-usability you need so you DRY (don't repeat yourself)
There are a few simple approaches based on the principle of what's simpler (versus what's perfectly DRY). Here's one alternative approach I use with CodeIgniter:
Instead of trying to load multiple controllers, reuse the view fragments from your search controller (or search route, depending which you're using). This requires using the same naming conventions for your data elements so the views are interchangeable, but you should be doing this anyway.
Instead of using multiple models for search, add a single Search model that knows about the things that can be searched on. If you want to prevent duplicate SQL, reuse the SQL between models (this can be done using constants, or loading SQL from disk).
Controllers are not great candidates for reuse from your own PHP code: they route actions and requests for resources to the things themselves. They are intended to be called via HTTP, using the URI interface you've come up with. Calling them from code is a coupling you want to avoid. That said, reusing controllers from JavaScript (or via cURL) is a great, decoupled way to reuse things in any web framework.