(Quick poll), which bug-tracker do you use?
May 21, 2010 8 Comments
| CARVIEW |
Filed under CakePHP
May 14, 2010 by teknoid 13 Comments
Update (05/21/2010): Well, looks like it has been removed from the core completely (good riddance).
Just a little tidbit of code that can help you when doing any AJAX communication…
Generally you’ll notice that cake inserts a timestamp with each response, unless debug = 0 (i.e. production mode).
This, of course, is not a desirable thing if you wish to return a proper JSON object, for example.
At least in 1.3 the SQL debug is moved into a separate element so it does not interfere with the output (you’ll need a little more adjustment for 1.2).
Now, we have to fix-up app/webroot/index.php to avoid timestamp output whenever we have an AJAX response.
Towards the end of the file make the following update:
if (Configure::read() > 0) {
if (!env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest") {
echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->";
}
Problem solved, enjoy ;)
Filed under CakePHP Tagged with cakephp ajax, jquery, cakephp 1.3
April 3, 2010 by teknoid 10 Comments
I know many of you have awesome blogs, which I have a hard time keeping up with due to … well, “life”.
If you wish to have to your blog listed in my “blogroll”, just shoot me a note and, after extremely thorough review (j/k, obviously), it will be added. (Hey, a few extra clicks never hurt anyone).
p.s. As much as I love spam and porn, please be sure it has at least something to do with web development. (Foreign… aka not English… languages are welcome).
April 1, 2010 by teknoid 14 Comments
What is your approach and strategy for building the views in your applications?
If it’s a-one-man show… well, that’s certainly is admirable, but often a little extra help is needed.
Let’s say we have a designer who generally does the visuals, the mark-up including some mock-up functionality and based on that I can add CakePHP features, but even more importantly all the rich-application jQuery programming, since ideally we’d like to keep CakePHP logic in the views to the bare-minimum…
Well… almost anything goes, but I’d love to see how are others handle similar situations in the MVC (well, forget the “M”) multi-developer environments.
Filed under CakePHP Tagged with CakePHP, jquery, rich application development, view development, cakephp views
March 22, 2010 by teknoid 37 Comments
You might have noticed that my posts are not as frequent as they used to be.
Granted, I try to be more elaborate (and boy do I have a few things baking in the oven at the moment).
So with this idea, I will open up this post as Q&A “type-of-thing”…
If you are having difficulty with something in CakePHP post away in the comments with your questions and I will do my best to solve it (or at least guide you in the right direction).
I do have a couple of requests/rules though:
Well that’s about it from my side. Hopefully this little endeavor will benefit both sides (I learn something new every day when I try to solve the problems for others… so yes, I am getting something out of it).
On top of that, I would like to reconnect with my readers, whom I felt neglected in the last few months (sorry, but work has to pay the bills and the habits).
Alright… let’s give it a shot… the forum is open :)!
Filed under CakePHP Tagged with CakePHP, q&a, question answer, cakephp help
March 10, 2010 by teknoid 12 Comments
First, I can’t believe I’ve missed a whole month of posting…. damn 28 days :(
Anyway, a recent post on the bakery https://bakery.cakephp.org/articles/view/dynamic-select-boxes-with-ajax-jquery prompted me to show a slightly more accurate approach on working with the given scenario.
(I don’t mean to piggy-back on someone’s work, but I feel it deserves a little “touch-up”).
If you don’t feel like reading the other post, the basic idea is to build a dynamic select list using CakePHP + jQuery.
For this example we’ll first select a car make and then build a select list of available models using jQuery.
In order to accomplish this, first of all, the appropriate association should be established between the models:
Car hasMany CarModel
Based on that we can have two controllers:
Next, of course, we’ll need some actions and views…
(The simple add/edit/etc… you could easily “bake”, so I’ll just focus on jQuery and relevant views at this point).
In CarsController we’ll add a list_models() method…
Now let’s take a look at the relevant view (list_models.ctp).
Again, here we are only focusing on the two drop-downs.
<?php $this->Html->script('views/cars/list_models.js', array('inline' => FALSE)); ?>
<?php
echo $this->Form->input('Car.name', array('empty' => 'Select One', 'options' => $names, 'id' => 'car-name'));
?>
<div id="car-models" style="display: none;">
<?php echo $this->Form->input('CarModel.name', array('type' => 'select', 'id' => 'car-model-name')); ?>
</div>
First, we’ll load up the jQuery script, which is relevant to the view. Despite my previous conventions, I find it much easier to replicate the structure of your JS file placement exactly as you’d do for the views. With one obvious difference, that all JS goes under /webroot/js/views/some_controller/same_as_view_name.js
You’ll notice that I wrapped the second select input into a div, which is hidden by default.
This is just one approach, but you certainly could leave it visible in your UI and populate it with an:
'empty' => 'Select Car First' … just a matter of choice here, I guess.
Next, comes our cars_controller.php:
I’m only showing the “interesting” actions.
public function list_models() {
$this->set('names', $this->Car->find('list'));
}
public function get_models_ajax() {
Configure::write('debug', 0);
if($this->RequestHandler->isAjax()) {
$this->set('carModels', $this->Car->CarModel->find('list',
array('conditions' =>
array('CarModel.car_id' => $this->params['url']['carId']),
'recursive' => -1)));
}
}
Let’s review the code a little… The list_models() method doesn’t really do anything special, it simply sets the car names to be used for the first select list in the view.
The get_models_ajax() will be called via jQuery in order to build our second select input. We are turning off debug here, so that any “extra” output does not mess with the returned data…
Yet, a side note… I am referring to SQL debug, officially produced by cake, or timestamp…
Keep the debug “on” and the resulting output (in case of errors) will be seen in the firebug console… and if you don’t have firebug, then I don’t know how to debug AJAX stuff.
Also, note the $this->params['url']['carId']. This value will come from our first select list, which lists the car names with the corresponding ID’s from the database. That is because we’ve previously established a proper model association, therefore finding all the models for a given car (car_id) is no trouble at all now. (Oh, and please don’t forget to include RequestHandler in your list of required components, see the manual for more info).
Next, we still need a view for our get_models_ajax() action. The purpose of that view would be to return all the $carModels, which as you see we are setting in the controller.
Here it is, get_models_ajax.ctp:
<?php
if(isset($carModels)) {
echo $this->Js->object($carModels);
}
?>
(Too much for such a simple task (view and all)?… well, respect MVC and it will not come back to bite you in the ass later.)
The view is not terribly interesting, but one thing to note is that $this->Js->object($carModels); will convert the array of data, which is returned by the find('list') in the controller, into a JSON object.
Mental note… You certainly don’t have to work with JSON and any type of data can be returned back to the client, but for simple AJAX communication between the client and the server I find JSON to be most convenient format.
Alright, last, but not least let’s see the jQuery snippet that makes all the magic happen.
list_models.js
$(document).ready(function(){
$('#car-name').live('change', function() {
if($(this).val().length != 0) {
$.getJSON('/cars/get_models_ajax',
{carId: $(this).val()},
function(carModels) {
if(carModels !== null) {
populateCarModelList(carModels);
}
});
}
});
});
function populateCarModelList(carModels) {
var options = '';
$.each(carModels, function(index, carModel) {
options += '<option value="' + index + '">' + carModel + '</option>';
});
$('#car-model-name').html(options);
$('#car-models').show();
}
Unfortunately it would take a few more days to explain every line of code in detail, and there are quite a few jQuery tutorials our there that will do a better job of explaining it, so I hope a little googl’ing will answer any outstanding questions.
… but I do want to point out a few things.
First, we are using jQuery’s handy $.getJSON, which does a GET request to a given URL with some data and returns the results back to our client.
Remember this piece: $this->params['url']['carId']? Well, that’s exactly where the carId value is coming from… i.e. the select input value, as specified between the curly brackets. Of course, there is no point in sending empty values to the server, therefore we wrap the entire chunk of AJAX code into if($(this).val().length != 0)… this will prevent jQuery making the extra call to the server if the “empty” option is selected.
Next, we already know that the data returned from the server will be a JSON object. So, before attempting to do anything with the returned data we check for some valid/good/existing data with:
if(carModels !== null)
In this example carModels is our JSON object, which is returned by CakePHP back to jQuery.
When all said and done, we use yet another awesome tool $.each to traverse the JSON object (i.e. carModels) and build our options list.
Finally, we add the freshly built HTML options list to the contents of our second select input and display it to the user.
We are pretty much done now, but just for some more detailed Q&A you can read further, if interested.
Q. Why use .live('change'... instead of just .change?
A. .live is a great tool to use if you are manipulating the DOM in some way and need to work with freshly inserted element. Granted in this example it is not necessary, but I wanted to show it off anyway. Just keep in mind that this approach is available and could be a life-saver at times.
Q. Why create populateCarModelList() function?
A. I like to keep things separated as much as possible, and who knows this function might come in handy for other reasons in a more complex application.
Q. Shouldn’t the get_models_ajax() action go into the CarModels Controller ?
A. Truth be told… it should. For the sake of simplicity I kept it in the same controller as the other method, but it would be “more proper” to place it in the CarModels Controller.
Q. Why did I assign DOM ID’s to the drop down elements, doesn’t cake do that automagically?
A. It does indeed, but cake’s DOM ID’s look like SomeModelThenField. In the world of CSS it is almost an unwritten rule that ID’s most often represented as some-model-then-field… so that’s my basic goal there. Thanks to a tip from Mark Story I promise to show in an upcoming post how to override the default CamelCasedID’s with dash-separated-ones.
Filed under CakePHP Tagged with CakePHP, jquery, cake 1.3, jquery dynamic selects
January 30, 2010 by teknoid 9 Comments
While it would be sweet, if the paginated data was usually not much more involved than a simple SELECT * FROM some_table… Unfortunately it is not.
In many cases there are a few, JOINs to related models, and likely a GROUP BY statement somewhere (maybe DISTINCT ?, who knows).
What I am getting at, is an SQL issue if that’s what you’d call it…
count(*) (which CakePHP uses properly, indeed) will yield pretty strange results if you have JOINs and GROUP BY (or any variation thereof… or “worse” yet, things like HAVING, etc.).
The display of records to the end-user will “disagree” with the number, which count(*) produces.
This is because count(*) actually counts everything (meaning across various models in the case of a JOIN).
The main issue comes up when one attempts to paginate the result-set from a complex query. You might see only 20 records displayed due to GROUP BY, but count(*) will actually return 43 (for example) because it does not actually count in the same way SELECT returns the records.
However, with a little help this problem is quite easily resolved…
Remember, we can always override the paginateCount() method inside a model.
For example, if we have an Order Model, just to get the expected count of records we’ll add:
public function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
if($this->statusId) {
$conditions = array('conditions' => array('Order.order_status_id' => $this->statusId));
}
$results = $this->find('count', array_merge(array('recursive' => -1), $conditions));
return $results;
}
With this scenario, we can get an accurate count of all orders, or just the ones that have some specific status.
The bottom line is that we know exactly what needs to be counted, and overriding paginateCount() allows us to do just that, regardless of what the actual record fetching query is like.
Filed under CakePHP Tagged with CakePHP, cakephp pagination, paginateCount, pagination, paginator
January 27, 2010 by teknoid 5 Comments
For a while now updateAll() would not trigger any of the model’s behavior callbacks.
This presents a problem for a couple of reasons:
updateAll() method allows to easily do things like field_value = field_value + 1, because unlike other similar methods it does not escape fields/valuesWhat we have below is a simple override (and a very rough patch at this point) that will trigger afterSave() of the attached behavior as expected.
Take the code below and place into the model, which uses updateAll() at some point.
public function updateAll($fields, $conditions = true) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$created = FALSE;
$options = array();
if($db->update($this, $fields, null, $conditions)) {
$created = TRUE;
$this->Behaviors->trigger($this, 'afterSave', array($created, $options));
$this->afterSave($created);
$this->_clearCache();
$this->id = false;
return true;
}
return FALSE;
}
Again, this works for my immediate needs and doesn’t break any tests. However there are a few things that can be improved to make it more consistent with other methods like save(), for example.
Well, either way I am hoping that this will help someone.
January 6, 2010 by teknoid 14 Comments
Common way to allow Auth access to all actions in a controller is to do something like this:
//In some controller
public function beforeFilter() {
$this->Auth->allow('*');
parent::beforeFilter();
}
However it can get pretty tedious if you’ve got lots of controllers and have to go through a bunch of them to enable (or disable) access.
Instead, try something like this in your AppController:
public function beforeFilter() {
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'index');
$this->allowAccess();
}
private function allowAccess() {
if(in_array($this->name, array('Pages'))) {
$this->Auth->allow('*');
}
}
The above will let Auth to access everything in the PagesController.
If you need to grant access to additional controllers simply add them to the array of names:
array('Pages', 'Books', 'Customers', 'Etc')
Having to deal with a single file to grant/deny access just makes things easier…
You could even make that “grantable” array a property of the AppController.
Filed under CakePHP Tagged with auth, CakePHP, cakephp auth
December 22, 2009 by teknoid 6 Comments
The infamous $cakeDebug variable, which has been around for a long time and managed to annoy a few people in the process, has been replaced in the recent builds of CakePHP 1.3 with an element.
See the ticket here:
https://cakephp.lighthouseapp.com/projects/42648/tickets/35-the-implementation-of-dbo_sourcephp-logging-needs-changed
The two points to take away are:
DboSource$cakeDebug in your layouts with echo $this->element('sql_dump');One of the obvious benefits, is that you no longer have to tweak the core to take control of the SQL debug. As always, simply place sql_dump.ctp in your own app (i.e. app/views/elements) and do what you wish with the output.
Just to test things out let’s throw the SQL debug into the Firebug console, rather than directly into the view.
We’ll need to modify the sql_dump.ctp (which now has been copied into our own app), just a little bit.
Around line 35, let’s replace the default output for SQL dump, for something like this:
//unchanged part of the sql_dump.ctp above this line
//here we are outputting the queries into the Firebug console
foreach($logs as $source => $logInfo) {
foreach($logInfo['log'] as $key => $query) {
echo $this->Html->scriptBlock('console.log("' . $query['query'] . '")');
}
}
For the real world example, this may not be a reasonable thing to do by any means… but it does show how easy it is now to output the SQL debug wherever you need it (logging, debugging, stylizing, parsing, sending to some remote destination… up to your imagination really).
Filed under CakePHP Tagged with $cakeDebug, cake sql debug, cakephp 1.3, modify sql debug