Calendar throughout CakePHP site, but CakeBook says to not use a Model in a Component - cakephp

I have a small calendar widget-type thing on many pages throughout my site. The goal is for it to retrieve events from X category that fall between Y and Z dates.
My assumption (I'm new to CakePHP) was that I should create a component, and have it do the query. Something like this:
class CalendarComponent extends Object {
var $uses = array('Event');
function getEvents($category = null, $date = null, $limit = null) {
$events = $this->Event->find('list', //conditions to get correct events
return $events;
}
}
BUT, according to this CakeBook page:
To access/use a model in a component
is not generally recommended
So - where would I store this logic / model call if not in a component? I've admittedly not used a component yet (or not created one anyway) due to lack of really understanding how I should use them - any snippet of advice on this is also VERY welcome.

Great question in my opinion and I imagine one that comes up quite often. I was actually dealing with a similar problem where I wanted some site-wide data gathering or functionality shoved into a component.
The first thing to keep in mind:
The book is a guideline.
These 'rules' aren't rules. If there's a good reason for breaking the rule and you understand why the rule is being broken then break the damn thing! Cake itself breaks this rule quite often.
Core components that require/use models:
Acl
Auth
Sessions (fairly positive you can save session data to a model)
So, clearly there are use cases where you need to use a model inside a component. How do you do it though?
Well, there's a couple different ways. What I wound up going with? Something like this:
<?php
ModelLoadingComponent extends Object {
public function startup($controller) {
$controller->loadModel('Model');
$this->Model = $controller->Model;
}
}
?>
That's it! Your component is now setup to use $this->Model...just like you would in a controller.
Edit:
Sorry, to clarify: no, you don't have to setup a new component to load models. I was showing an example for how you could load a component in any model. The startup function I used is a component-specific callback, there's a whole slew of them http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components. These callback methods make components a lot easier to work with. I highly recommend looking at this part of the components tutorial if nothing else.
If you were working inside an AppController object you could call $this->loadModel() but we aren't working an AppController! We're working with a component, really an Object. There is no Object::loadModel() so we have to come up with a different way to get that model. This is where $controller comes into play in our startup callback.
When the startup method is invoked by Cake it will pass the $controller object it's working with on this dispatch as the first parameter. With this we're able to access controller methods...like loadModel().
Why do we do it this way?
Well, we could use ClassRegistry::init('Model') in each of our component methods that need to use the model. If you have 10 methods in your component and only 1 of them uses the model this might work. However, what if you have 10 methods in your component and all 10 of them use the model? Well, you'd be calling ClassRegistry::init('Model') 10 times! That's a lot of overhead when what you really want is just 1 model object. With this method the component is creating one model object. The one we create in startup.
I hope this clarifies your questions and provides some insight into why I use this method for models in components.
Edit: Added a code clarification after I did some experimenting.

I think writing a component is overkill in this case and it would be cleaner to put the getEvents method into the Event model.

Related

Cakephp Standard - Is it the Proper standard to write things in controller

Sorry for this basic question. The following are the codes presented in my Userscontroller.php
public function register()
{
//Setting some data
$this->User->create();
$this->User->save($this->request->data)
}
public function edit()
{
//Setting some data
$this->User->save($this->request->data)
}
public function admin_add()
{
//Setting some data
$this->User->create();
$this->User->save($this->request->data)
}
public function admin_edit()
{
//Setting some data
$this->User->save($this->request->data)
}
One of my senior reviewed this code and said that the above code not met the CAKEPHP Standard..Business logic need to move to model
like as follows in model
<?php
class User extends AppModel {
//Validation parts
public functions savingData($data =array(), $id=false)
{
if($id == false)
{
$this->create();
}
$this->User->save($data)
}
}?>
And in controllers he is asking to call this savingData function for Create and update options
in MVC, business rules goes to model.
but in your example there is no any business logic so your code is correct to be in controller.
I suggest to use cakephp console to create controller and model and you can get it by yourself.
Usually any method which has validations or save data to other tables or any extra functionality that makes the actions bigger than one page you need to consider it (break the function to smaller functions) and you can put this function base on your need and access into a Model or Component. I can say fat model concept and Component is used for this kind of situation.
I believe that methods shouldn't be more than one page this make the application maintainable for the next developer.
Also any static methods can be inside Utility as a class which can be called from controllers or views ... easily.
Bear in mind that Cake has lots of model behaviours which contained in all models. like beforeSave(), afterSave(), initiate(), before and after delete() ,...
At the end it is entirely up to you how you implement OOP, tidy and maintainable application.
Other example imagine a Car.
All the engine and complected stuff is inside box where you can't see it you can call it Model .
Controller stuff is in front of your hand so you can Controller it. Controller
Hope it helps if any one can add more option to this answer for others to learn.
Generally speaking your boss is correct in that the goal is fat models - skinny controllers. The reason for this is that if at your work they are doing Unit testing ( which I hope they are ) this makes things MUCH easier on you. Testing in the controller is a pain in the rear.
With that being said though the code in your controller is negligible so don't know if what he is asking is "neccessary" exactly, just technically not consistent with current standards ( which will change with time anyway ). Bottom line if you are bothered that he/she made this criticism, try not to be. Things like this are not written in stone and are left up to interpretation for the most part.

Cakephp how should you manage controllers and views

I am completely new to CakePHP, therefore I am fairly confused on how the management of different controls/views/elements works.
The Problem:
I am attempting to create my home page, which has PageController and view home.ctp. Now, I have another controller, IdeaController, which contains function to load all the ideas from the DB.
public function index() {
$this->set('title_for_layout', 'Ideas');
$this->set('ideas', $this->Idea->find('all'));
}
Now, I need to access this Idea Controller from multiple(up to 3) different views. What is the best way to go about doing so?
Thank-you
You're going the other way around...
Views don't "have access" to the controller. The controller receives a request, tells the models what info it need from them, and passes all necessary data to the views. Views are like little dolls you can put clothes on, but nothing more, they don't speak or play or ask random questions to you.
Generally speaking, there's just one view per action in a controller. If you need to "access" the controller from different views, you may be a little confused with cake.
Without knowing the exact reason you need to do this, I'm going to assume you want to "access" the controller from 3 different views only to get all ideas from the BD. If that's the case, the standard way is:
1) Create a function in the appropriate model:
//Idea Model
public function getAll() {
return $this->Idea->find('all');
}
Well, you don't actually need to do that function, since it's so simple, but for more complex cases where you want to get only the ideas of the logged in user and in a certain order, encapsulating that in the model is a mentally-sane way to go.
2) In every action in the controller that the view will need that data, you do
public function randomAction() {
$this->set('ideas', $this->Idea->getAll());
}
3) In every view you receive the data as
pr($ideas);
And that's it.
The views never "access" the controller, so your question turns out to be a bit confusing. Maybe you meant having 3 views associated to the same action, but there's little reason to do that also... Or maybe you meant to have a "view" in a lot of other "views", like the "info" bar on stackoverflow, that repeats everywhere so you need to have it in a lot of views. If that was the case, then you have to do that in an element to reutilize that "view"-code inside other views.
If you clarify a little more what you want, I can probably answer a lot better, but I hope at least I gave you some direction on where to look or how to go about what you want to do.
Each View has to have it's own method in the Controller.
Say, if you want an ideas/index, ideas/view/x and ideas/add pages, you need to create the following methods:
class IdeasController extends AppController
{
public function index()
{
/* index stuff */
}
public function view($id)
{
/* view stuff */
}
public function add()
{
/* add stuff */
}
}
For the sake of understanding the framework, is advisible to do the blog tutorial, as you after that will be very familiar with the core concepts: http://book.cakephp.org/2.0/en/getting-started.html#blog-tutorial
Good luck!

Data logic on load using a component - cakePHP

I have a project I'm developing which includes articles that can be commented on (comments stored in separate table of course). I want to perform pre logic on a field from each comment, wherever they are loaded through-out the app. The data logic I want to performed is from a custom written component.
The logical place to me that this could be achieved globally is from the comment model, but I could be wrong.
I'm not even 100% if I can use a component from a model, but I've been trying to do this logic using the afterFind() call-back function:
function afterFind($results) {
foreach ($results as $key => $val) {
if (isset($val['Comment']['created'])) {
$results[$key]['Comment']['created'] = $this->Dateconvert->howLongAgo($val['Comment']['created']);;
}
}
return $results;
}
I have tried echoing from inside this function and it doesn't actually seem to be getting called but searching hasn't revealed any functions that do, but I believe afterFind() is best to illustrate what I'm trying to achieve.
So I am looking for a solution where I can performed the post-load logic on articles comments, whether they are being loaded from other controllers with associations to comments or in the comments controller. Basically a global one hit solution :D
cakephp indicates that components are for controllers and behaviours for models and helpers for view...
knowing that first, you may also know that you can use any part of it wherever you want because cake still php, though is not recomended... if is a library of functions you may want to put it inside the libs folders and access it from there.
how, easy use App::import('component', 'nameComponent'); component can be lib, controller, etc..
Having said that, afterFind is a good place to do after load things, remember that this function is call ONLY when a find is used, if you use, any other like query or save or update it won't be called.
hope this helps you :)

Help with cake controllers

We had an outsourced engineer work on a quick feature DELETING items listed in our database. He says that the code is difficult because the "controller" is missing. Is there a pre-loaded controller for every function like that in cake, or is it weird that he is expecting a controller to be there for a feature we didn't have yet.
There is a generic AppController, but that's more of an abstract class in practice (you generally derive your other controllers from that).
It's not that weird at all that he's expecting a controller -- after all, you won't be able to call methods in the models (which is how I'm guessing you're doing delete) unless you have a point of control to call them from. In this case, the point of control is the controller.
So you can just create a controller. Here's a template to start from:
class SomeController extends AppController {
function delete() {
$this->Some->delete();
}
}
Then access /somes/delete (remember, URLs are generally /controller/action).
Now, he could be talking about the Cake Bake CLI app. That will take your DB tables, and walk you through an initial basic setup for your app. Generally it creates a basic skeleton for CRUD actions.
Either way, you need to create a controller (manually, or via Bake).
When you use the Cake bake function, it'll create all the controllers for you. When you don't use it, you'll need to create them manually. It makes no sense to make all the controllers at the begin, just make them when you really gonna write them would be good.
If you do not have a controller in CakePHP when you visit a page (http://www.youraddress.com/Newfeature) you receive a missing controller error:
Error: NewfeatureController could not be found.
Error: Create the class NewfeatureController below in file: app\controllers\newfeature_controller.php
You cannot get or delete data from the database without controllers - Understanding Model-View-Controller. You do not need the controller only for static pages in CakePHP.

Is using the RequestHandlerComponent in a model possible?

I'm new to PHP and decided to use the cakePHP framework to help me get started.
I can't figure out one thing though, I want to call methods on the RequestHandlerComponent class to update a users last used IP address and other information, I figured the best place to put this would be in the beforeSave() method on the User model.
I can't figure out how to call the getClientIP method though.
The normal code that would otherwise go in the controller doesn't work. Is there another way to call this class if you're in the model and not the controller?
Class Level:
var $components = array('RequestHandler');
And in the function:
$this->data['User']['lastActiveIP'] = $this->RequestHandler->getClientIP();
Gives:
Undefined property: User::$RequestHandler
Call to a member function getClientIP() on a non-object
Components, by design, aren't available to models (without bypassing MVC convention - which you can do, of course). If you chose to force it to be available, look into ClassRegistry::init(). A better solution, I think, would be to employ the RequestHandler component in your controller (where it's meant to be used), set the lastActiveIp value in the controller (exactly as you've shown in your own example code) and pass the entire data array along to the model.
Now your component is being used where it should be and the model gets to remain ignorant about where it gets its data. At the risk of oversimplification, all the model should know is what to do with the data once it arrives; let the controller worry about collecting and packaging the data.
In addition to Rob's answer, maybe it's enough to put a bit of code together yourself that uses the general env('REMOTE_ADDR') or similar variables. Look at the RequestHandler code, it's not doing anything terrifically complicated.
You may even be able to call the component statically, which is slightly better than instantiating it in the model (still in violation of MVC though). Untested, but should work:
App::import('Component', 'RequestHandler');
RequestHandlerComponent::getClientIp();

Resources