<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>webbricks &#187; CakePHP</title>
	<atom:link href="http://blog.grzegorzpawlik.com/tag/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.grzegorzpawlik.com</link>
	<description>Doświadczenie, to coś, co zdobywamy tuż po chwili w której było nam potrzebne ...</description>
	<lastBuildDate>Tue, 07 Feb 2012 10:09:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Namespace’y javascript w widokach cakePHP</title>
		<link>http://blog.grzegorzpawlik.com/2010/10/namespace-javascript-w-widokach-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/10/namespace-javascript-w-widokach-cakephp/#comments</comments>
		<pubDate>Fri, 29 Oct 2010 17:00:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[JS and friends]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[namespace]]></category>
		<category><![CDATA[View]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1410</guid>
		<description><![CDATA[Czasem zdarza się tak, że w danym widoku potrzebuję bardzo specyficzną funkcję javascript. Na przykład w widoku invoices/add funkcja count_gros_value, która na podstawie wpisanej wartości w polu netto (net) i stawki var wyliczy wartość brutto (gros). Załóżmy, że nie wiem &#8230; <a href="http://blog.grzegorzpawlik.com/2010/10/namespace-javascript-w-widokach-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Czasem zdarza się tak, że w danym widoku potrzebuję bardzo specyficzną funkcję javascript. Na przykład w widoku invoices/add funkcja count_gros_value, która na podstawie wpisanej wartości w polu netto (net) i stawki var wyliczy wartość brutto (gros).</p>
<p>Załóżmy, że nie wiem jak napisać taką funkcję, żeby była elastyczna, po prostu we wnętrzu mam zaszyte id pól formularza:</p>
<pre name="code" class="javascript">
function count_gros_value(){
   $("#Invoice[gros]").val(
      parseFloat($("#Invoice[net]").val()) *
      parseFloat($("#Invoice[vat]").val())
   );
}
// wywołanie
count_gros_value();
</pre>
<p>(nie sprawdzałem tego kodu, służy jako przykład i nie mam pojęcia czy dobrze działa).</p>
<p>Oprócz wielkiej nieelastyczności kodu (o czym teraz pisał nie będę) istnieje problem polegający na tym, że nie wiem czy jakiś inny programista nie zdefiniował funkcji count_gros_value gdzieś w layoucie. Oczywiście dostanę error jeśli tak jest, więc poprawię ją na my_count_gros_value() (sic!). Ok, wszystko działa. Jednak przychodzi inny programista i odczuwa potrzebę zdefiniowania funckji my_count_gros_value() w layoucie (no bo count_gros_value() już istnieje, a jego robi coś inaczej/lepiej więc trzeba ją zaimplementować). W ten sposób rozwala twój zaimplementowany fragment i nikt tego nie zauważa.</p>
<p>Jak tego uniknąć? Można zacząć nazywać takie &#8220;lokalne&#8221; funkcje invoices_index_count_gros_value() ale to zaczyna wyglądać nieestetycznie i aż się ciśnie na usta magiczne słowo &#8220;namespace&#8221;.</p>
<p>W Javascript nie ma przestrzeni nazw, ale możesz użyć obiektów:</p>
<pre name="code" class="javascript">
var invoices = {
   index: {
      count_gros_value: function(){
         $("#Invoice[gros]").val(
            parseFloat($("#Invoice[net]").val()) *
            parseFloat($("#Invoice[vat]").val())
         );
      }
   }
}
// wywołanie
invoices.index.cunt_gros_value();
</pre>
<p>to jest już fajniejsze, ale ma dwa problemy:</p>
<ol>
<li>ciągłe definiowanie obiektów dla namespace&#8217;ów jest równie uciążliwe jak nazwa funkcji invoices_index_count&#8230;</li>
<li>gdy ktoś przed nami zdefiniuje zmienną var invoices = &#8220;invoices&#8221;; to mu ją nadpiszemy</li>
</ol>
<p>Dlatego proponuję taką funckję, która definiuje dowolnie głębokie obiekty udające namespace&#8217;y i do tego tworzy je wszytkie w zmiennej o pseudolosowej nazwie (w stylu: Namespace12883495048110.1425782083547854):</p>
<pre name="code" class="javascript">
function Namespace(namespace_path){
	var w = window;

	window.namespaceFrameworkNamespaceName =
		window.namespaceFrameworkNamespaceName ||
		"Namespace"+ new Date().getTime() + new Math.random();

	var arr  = (window.namespaceFrameworkNamespaceName + namespace_path).
						split(".");

	for(i in arr){
		w[ arr[i] ] = w[ arr[i] ] || {};
		w = w[ arr[i] ];
	}
	return w;
}
</pre>
<blockquote><p>Ok, jakiś uparciuch mógłby się uprzeć, że ktoś może w aplikacji zmiennej &#8216;namespaceFrameworkNamespaceName&#8217; &#8211; takiego problem nie rozwiązywałbym na poziomie kodu, ale personalnym (zwiałbym z projektu) ;)</p></blockquote>
<p>tak wygląda definiowanie funkcji w namespace:</p>
<pre name="code" class="javascript">
Namespace("app.users.index").check_form = function(){
	document.write("my_check_form()");
};
</pre>
<p>a tak jej wywołanie:</p>
<pre name="code" class="javascript">
Namespace("app.users.index").check_form();
// lub gdy mamy zamiar wywołać wiele funkcji z danego namespace:
with(Namespace("app.users.index")){
 check_form();
}
</pre>
<p>Można by z tego zrobić tez fajne rozszerzenie jQuery,<br />
$().nspc(&#8220;app.invoices.index&#8221;) (pisałem już o <a href="/?p=1222">rozszerzaniu jQuery w tutorialu &#8220;longPolling&#8221;</a>)</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/10/namespace-javascript-w-widokach-cakephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lazy Loading w modelach a&#8217;la CakePHP</title>
		<link>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/#comments</comments>
		<pubDate>Tue, 19 Oct 2010 14:29:07 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Inne]]></category>
		<category><![CDATA[lazy loading]]></category>
		<category><![CDATA[model]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[PHP5]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1383</guid>
		<description><![CDATA[CakePHP nie wspiera aktualnie leniwego ładowania obiektów. W prawdzie są jakieś skrypty, ale jeszcze żaden z nich nie chciał mi zadziałać. Zacząłem się zastanawiać: &#8220;Gdybym miał zaimplementować Lazy Loading w warstwie modelu, to jak bym to zrobił?&#8221; To co mi &#8230; <a href="http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>CakePHP nie wspiera aktualnie leniwego ładowania obiektów. W prawdzie są jakieś skrypty, ale jeszcze żaden z nich nie chciał mi zadziałać. Zacząłem się zastanawiać: &#8220;Gdybym miał zaimplementować Lazy Loading w warstwie modelu, to jak bym to zrobił?&#8221; To co mi wyszło korzysta z rozwiązań PHP5, więc może wsparcie gałęzi 1.x Cake&#8217;a dla PHP4 implikuje brak tego wzorca?</p>
<p>Tak czy siak, podzielę się z Wami moimi pomysłami.</p>
<p>Zacznijmy od początków. Przeciwieństwem leniwego ładowania jest ładowanie gorliwe (lub zachłanne), to znaczy ładowanie na zapas wszystkiego co może okazać się potrzebne. Warstwa modelu w cake&#8217;u mogła by wyglądać tak (duże uproszeczenie, jedyna możliwa relacja to belongsTo).</p>
<h4>Eager Loading</h4>
<pre name="code" class="php">

class Model {
	function find(){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."`";

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
		if(isset($this->belongsTo) &#038;&#038; is_array($this->belongsTo)){
			foreach($this->belongsTo as $related){
					$this->{$related} = new $related();
			}
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group");
}

class Group extends Model{
}

$user =  new User();

$user->find();
echo "<br/>";
$user->Group->find();
</pre>
<p>Widzimy, że obiekt Group jest inicjowany długo przed użyciem:</p>
<pre>
creating User
creating Group
calling User::find()

string 'Select * from `users` as `User`' (length=31)

calling Group::find()

string 'Select * from `groups` as `Group`' (length=33)
</pre>
<p>Mało tego, ładuje się nawet jeśli nigdy nie wywołamy metody w tym obiekcie (zakomentuj $user->Group->find(); żeby się o tym przekonać).</p>
<h4>Lazy Loading</h4>
<pre name="code" class="php">
class Model {
	function find(){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."`";

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
	}

	function __get($property){
		if(!isset($this->{$property})) {
			$this->{$property} = new $property();
		}
		if(in_array($property, $this->belongsTo)){
			return $this->{$property};
		}else{
			$r = new ReflectionClass($this);
			throw new Exception(
				"$property not related with {$r->getName()}"
			);
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group");
}

class Group extends Model{
}

$user =  new User();

$user->find();
echo "<br/>";
$user->Group->find();
</pre>
<p>W tym rozwiązaniu obiekt Group nie zostanie stworzony, dopóki nie będziemy go potrzebować. Wykorzystana jest do tego magiczna metoda __get, która jest wywoływana wtedy, gdy próbujemy się odnieść do atrybutu obiektu, który nie istnieje.</p>
<p>ale pozostaje kwestia wyszukiwania w modelu tak, żeby zwrócił powiązane dane<br />
np.: var_dump($user->find());</p>
<pre>
array
  0 =>
    array
      'User' =>
        array
          'id' => int 1
          'name' => string 'Franek' (length=6)
      'Group' =>
        array
          'id' => int 1
          'name' => string 'Admin' (length=5)
  1 =>
    array
      'User' =>
        array
          'id' => int 2
          'name' => string 'Janek' (length=5)
      'Group' =>
        array
          'id' => int 10
          'name' => string 'User' (length=4)
</pre>
<p>Wiemy, że belongsTo oznacza, że User powinien mieć pole (group_id), zatem query powinno wyglądać mniej więcej tak:</p>
<pre name="code" class="sql">
Select User.id, User.name, Group.id, Group.name from users as User
Inner Join groups as Group on (User.group_id = Group.id)
</pre>
<p>W tej chwili cakephp zawiera behavior containable, jednak on polega na załadowanych już modelach.</p>
<p>Zostańmy przy terminologii cake&#8217;a i przyjmijmy, że będziemy przekazywać informacje na temat powiązanych elementów w zmiennej $contain</p>
<p>function find($contain=null)</p>
<p>Teraz chciałbym powiedzieć: obiekcie $user zwróć mi wszystkich użytkowników z bazy wraz z informacją w jakich są grupach:</p>
<pre name="code" class="php">
$user =  new User();

$user->find(
	array(
		"belongsTo" => array($user->Group)
	)
);
</pre>
<p>Zatem zaktualizuję metodę find:</p>
<pre name="code" class="php">
	function find($contain=null){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "Select * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."` \n";

		if(!empty($contain) &#038;&#038; is_array($contain["belongsTo"])){
			foreach($contain["belongsTo"] as $belongsToModel){
				$query .= $belongsToModel->getBelongsToJoin($this);
			}
		}

		var_dump($query);

		//return query result
	}
</pre>
<p>która od teraz zakłada, że model posiada metodę, która potrafi utworzyć odpowiedniego joina:</p>
<pre name="code" class="php">
	function getBelongsToJoin($obj){
		$owner_reflection = new ReflectionObject($this);
		$obj_reflection = new ReflectionObject($obj);
		return "INNER JOIN `".strtolower($owner_reflection->getName())."s`
					as `".$owner_reflection->getName()."`
						ON (`".$owner_reflection->getName()."`.`id` = `".$obj_reflection->getName()."`.`".strtolower($owner_reflection->getName())."_id`)
					";
	}
</pre>
<p>to jest oczywiście bardzo brzydkie ze względu na refleksje. Metodę getName() można jednak zaimplementować w klasie Model.</p>
<p>Tak czy siak, w tym wypadku nawet w find()&#8217;zie uwzględniającym relacje można stosować Lazy Loading</p>
<p>Na koniec całość kodu, z większą ilością relacji i przykładem działania (i małym refactoringiem &#8211; skoro już mowa o lazy loading to głupio było ładować piętnaście razy ReflectionClass):</p>
<pre name="code" class="php">

class ModelException extends Exception{}

class Model {

	protected $reflection = null;

	function getName(){
		if(is_null($this->reflection)){
			$this->reflection= new ReflectionClass($this);
		}
		return $this->reflection->getName();
	}

	function getTableName(){
		return strtolower( $this->getName() . "s" );
	}

	function getFKName(){
		return strtolower( $this->getName() . "_id");
	}

	function getBelongsToJoin($obj){
		$owner_reflection = new ReflectionObject($this);
		$obj_reflection = new ReflectionObject($obj);
		return "INNER JOIN `".$this->getTableName()."`
					as `".$this->getName()."`
						ON (`".$this->getName()."`.`id` = `".$obj->getName()."`.`".$this->getFKName()."`)
					";
	}

	function find($contain=null){

		$r = new ReflectionClass($this);
		print "calling ".$r->getName()."::find($conditions)";

		$query = "\nSelect * from `".strtolower($r->getName())."s` ".
					"as `".$r->getName()."` \n";

		if(!empty($contain) &#038;&#038; is_array($contain["belongsTo"])){
			foreach($contain["belongsTo"] as $belongsToModel){
				$query .= $belongsToModel->getBelongsToJoin($this);
			}
		}

		var_dump($query);

		//return query result
	}

	function __construct(){
		$r = new ReflectionClass($this);
		print("creating ".$r->getName()."<br/>");
	}

	function __get($property){
		if(!isset($this->{$property})) {
			$this->{$property} = new $property();
		}
		if(in_array($property, $this->belongsTo)){
			return $this->{$property};
		}else{
			$r = new ReflectionClass($this);
			throw new ModelException(
				"$property not related with {$r->getName()}"
			);
		}
	}
}

class User extends Model{
	protected $belongsTo = array("Group", "Unit", "Company");
}

class Group extends Model{
}

class Unit extends Model{}
class Company extends Model{}

$user =  new User();

$user->find(
	array(
		"belongsTo" => array($user->Unit, $user->Company)
	)
);
</pre>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/10/lazy-loading-w-modelach-ala-cakephp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Long Polling – tutorial, part 2</title>
		<link>http://blog.grzegorzpawlik.com/2010/10/long-polling-tutorial-part-2/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/10/long-polling-tutorial-part-2/#comments</comments>
		<pubDate>Mon, 11 Oct 2010 17:00:25 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1331</guid>
		<description><![CDATA[how to mimic &#8220;server push&#8221; with &#8220;client pull&#8221;? As You could already see at wikipedia, long pooling means just requests (in our case ajax requests) which can wait quite a long time for server response. Server doesn&#8217;t send a single &#8230; <a href="http://blog.grzegorzpawlik.com/2010/10/long-polling-tutorial-part-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h4>how to mimic &#8220;server push&#8221; with &#8220;client pull&#8221;?</h4>
<p>
As You could already see at <a href="http://en.wikipedia.org/wiki/Push_technology#Long_polling">wikipedia</a>, long pooling means just requests (in our case ajax requests) which can wait quite a long time for server response. Server doesn&#8217;t send a single bit of information until something interesting (for the request) appears.
</p>
<p>
There are two possible results:</p>
<ol>
<li>something appears (in our case &#8211; new post in database), so server sends this info, request hits a success and do something with received data</li>
<li>nothing appears for some time, and request time-outs.</li>
</ol>
<p>
Both possible paths ends the same &#8211; request starts another one to wait for something to happen.
</p>
<p>
You probably already have some ideas how to solve it. If so &#8211; please try it (and if it differs from mine solution &#8211; I&#8217;d love to see it, please post it in comments.)
</p>
<p>
There are some pitfalls, though. I&#8217;ll show most of them.
</p>
<p><h4>assumptions</h4>
<p>Thing to remember at this point is where request are always going. So it&#8217;s not a good way to send html code back and forth. <a href="http://en.wikipedia.org/wiki/Push_technology#Long_polling">XHttpRequests</a> should expect <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> responses (cleaned of white-spaces if possible).<br />
Another conclusion is: if there&#8217;s nothing to send from server it&#8217;s better for xhr to timeout than receive response &#8220;nothing new&#8221;. Even though this is not elegant solution (time-out is still some kind of error).
</p>
<p>
We should decide how much of information should be send to server in order to get new messages in the thread. The minimum is the thread&#8217;s root id and last message id (biggest one). With that information it is possible to get all messages that are in the subtree which is rooted in particular id and their id is bigger than the very last one. But in that case we at least need to make two queries: one to get root lft and rght values, second to get all his children (which ids are between roots lft and rght).
</p>
<p>
The second approach is to send all messages ids in thread and just get all messages that have parent_id in that set.
</p>
<p>
My approach is that one should use cleaner solution (1st one) because we don&#8217;t have enough data to decide which one is faster (when the application is in production mode You could do some research. Of course we could make some fancy simulations but it is not the point of this tutorial).
</p>
<p>
So create an action that takes two parameters (tree root id and last message id in the thread) and sending back new messages (if any) or do nothing (not even sending empty response).
</p>
<p><pre name="code" class="php">
//controllers/posts_controller.php
	function get_new($rootId, $lastMessageId){
		$root = $this->Post->read(null, $rootId);

		$posts = array();

		while(empty($posts)){
			$posts = $this->Post->find(
				"all",
				array(
					"conditions"=>array(
						"and"=> array(
							"Post.lft > " => $root["Post"]["lft"],
							"Post.lft < " => $root["Post"]["rght"],
							"Post.id >" => $lastMessageId
						)
					)
				)
			);
			if(empty($posts)){
				sleep(2); // sleep for two seconds
			}
		}
		$this->set("posts", $posts);
		$this->render("get_new", false);
	}

// views/posts/get_new.ctp
<?php echo json_encode($posts); ?>
</pre>
</p>
<p>
You can try it out in Your browser. But if You choose /posts/get_new/1/8 when 8 is the very last message in this thread it would choke your server up ;) For testing use parameters where at least one message will appear (in the example: /posts/get_new/1/7).
</p>
<h4>Server issue</h4>
<p>
This clash on server is connected to sessions. Apache waits to the end of every scripts until it saves session data, at the end it implicitly closes session. For some reason Apache cannot release thread for this request if session is not closed (even if You hit cancel in Your browser, what triggers kill for the processing request in the most of the cases). What you need is to add 	session_commit(); at the very beginning of actions which are long polled. (if You need to write to session &#8211; do it as fast as it&#8217;s possible before while loop).</p>
<pre name="code" class="php">
//controllers/posts_controller.php
	function get_new($rootId, $lastMessageId){
		session_commit();
		$root = $this->Post->read(null, $rootId);
		//...
</pre>
</p>
<p>
Now we have nice action which gives us JSON string with new messages. Lets load them to our thread.
</p>
<p>
First of all we&#8217;ll need root id and last post id stored in Thread object. Root id is in the first element&#8217;s parent_id, last post we&#8217;ll find while generating thread tree.<br />
Let&#8217;s add those properties to Thread class:</p>
<pre name="code" class="javascript">
//webroot/js/thread.js
(function(window, document, udefined){
	window.Thread = function(_posts) {
		this.posts = _posts;
		this.rootId = false; //<==
		this.lastId = false; //<==
		this.extendJQuery();
		return this;
	};

})(window, document);
</pre>
</p>
<p>
Store root's and last message ids in createThread function:</p>
<pre name="code" class="javascript">
//webroot/js/thread.js
Thread.prototype.createThread = function(){
	if(this.posts.length <1){
		return this;
	}
	this.rootId = this.lastId = posts[0].Post.parent_id; //<==
	var _this = this; //need to remember this, because in each method this means actual object from collection we iterate through
	$(this.posts).each(function(k, v){
		if($("#"+v.Post.parent_id).find("ul").length == 0){
			if($("#"+v.Post.parent_id).find("div.reply").length == 0){
				_this.createReplyDiv().appendTo("#"+v.Post.parent_id);
			}
	        $("
<ul>", {class: "posts children"}).appendTo("#"+v.Post.parent_id);
		}
	     $("
<li>", {
	    	  id: v.Post.id,
	    	  innerHTML: "
<div>"+v.Post.message+"</div>

"
	    	  ,
	    	  class: "post child"
	     }).
	     append(_this.createReplyDiv()).
	     appendTo("#"+v.Post.parent_id+">ul");
	     if(v.Post.id>_this.lastId){ //<==
	    	 _this.lastId = v.Post.id;
	     }
	});
	return this;
};
</pre>
</p>
<p>
Now I want to get data from get_new action, but my thread object have no idea where to get it from. Let's allow to pass endpoint url to constructor.</p>
<pre name="code" class="javascript">
//webroot/js/thread.js
(function(window, document, udefined){
	window.Thread = function(_posts, _updateEndpoint) {//<==
		this.posts = _posts;
		this.rootId = false;
		this.lastId = false;
		this.updateEndpoint = _updateEndpoint; //<==
		this.extendJQuery();
		return this;
	};

})(window, document);
</pre>
</p>
<p>
update Thread object creation:</p>
<pre name="code" class="javascript">
//views/layouts/default.ctp
		$(document).ready(function(){
			if(!window.posts) return false;
			window.thread = new Thread(
				posts,
				"&lt;?php echo $html->url(
							array(
							'controller'=>'posts',
							'action'=>'get_new')
				);?&gt;"
			).createThread();
			console.log(thread);
		});
</pre>
</p>
<p>
and create update() method. It should get JSON with new posts, and in case of either success or time-out Thread.update() should be called again:</p>
<pre name="code" class="javascript">
//webroot/js/thread.js
Thread.prototype.update = function(){
	if(!this.updateEndpoint || !this.rootId || !this.lastId){
		alert("Thread corrupted");
		return false;
	}
	_this = this;
	$.ajax({
		url: this.updateEndpoint +"/"+ this.rootId+ "/" + this.lastId,
		dataType: 'json',
		timeout: 5000,
		success: function(){
			_this.update();
		},
		error: function(){
			_this.update();
		}
	});
	return true;
}
</pre>
</p>
<h4>bug #1</h4>
<p>
I've found a bug after adding post with id=10 to the thread. It appears that I was storing lastId - the biggest id but in... dictionary order (check in JS: "10">"9" => false). Please patch last if statement in Thread.addPost() like that:</p>
<pre name="code" class="javascript">
//...
		if(parseInt(v.Post.id)>_this.lastId){
			_this.lastId = parseInt(v.Post.id);
		}
}
</pre>
<p>Call newly created method in Thread.createThread():</p>
<pre name="code" class="javascript">
Thread.prototype.createThread = function(){
	var _this = this;
	if(this.posts.length <1){
		return this;
	}
	this.rootId = this.lastId = posts[0].Post.parent_id;
	$(this.posts).each(function(k, v){
		if($("#"+v.Post.parent_id).find("ul").length == 0){
			if($("#"+v.Post.parent_id).find("div.reply").length == 0){
				_this.createReplyDiv().appendTo("#"+v.Post.parent_id);
			}
        	$("
<ul>", {class: "posts children"}).appendTo("#"+v.Post.parent_id);
		}

		$("
<li>", {
			id: v.Post.id,
			innerHTML: "
<div>"+v.Post.message+"</div>

",
			class: "post child"
		}).
		append(_this.createReplyDiv()).
		appendTo("#"+v.Post.parent_id+">ul");
		if(parseInt(v.Post.id)>_this.lastId){
		 _this.lastId = parseInt(v.Post.id);
		}
	});
	this.update(); //<==
	return this;
}
</pre>
</p>
<p>
You can check now if it's trying to get new data again and again. Try to add new post in other browser and see what happens with request...<br />
As You can see - there's ok response but from now on every request is getting the same response, and the thread isn't updated. We'll fix that now. First extract the method that creates post elements from createThread method - we will reuse it...</p>
<p>
Thread.createThread should look like that:</p>
<pre name="code" class="javascript">
Thread.prototype.createThread = function(){
	if(this.posts.length <1){
		return this;
	}
	this.rootId = this.lastId = posts[0].Post.parent_id;
	var _this = this; //need to remember this, because in each method this means actual object from collection we iterate through
	$(this.posts).each(function(k, v){
		_this.addPost(v.Post.id, v.Post.parent_id, v.Post.message); //<==
	});
	this.update();
	return this;
};
</pre>
</p>
<p>
and our new method Thread.addPost:</p>
<pre name="code" class="javascript">
Thread.prototype.addPost = function(id, parentId, message){
	if($("#"+parentId).find("ul").length == 0){
		if($("#"+parentId).find("div.reply").length == 0){
			this.createReplyDiv().appendTo("#"+parentId);
		}
        $("
<ul>", {class: "posts children"}).appendTo("#"+parentId);
	}
     $("
<li>", {
    	  id: id,
    	  innerHTML: "
<div>"+message+"</div>

"
    	  ,
    	  class: "post child"
     }).
     append(this.createReplyDiv()).
     appendTo("#"+parentId+">ul");
     if(id>this.lastId){
    	 this.lastId = id;
     }
}
</pre>
</p>
<h4>bug #2</h4>
<p>
I encountered even more important bug - we caused a memory leak in our server (just wait long enough with get_new requests running) - after xhr time-out the script is not terminating.<br />
I added a simple counter to get_new method:</p>
<pre name="code" class="php">
//controllers/posts.php
	function get_new($rootId, $lastMessageId){
		//...
		$counter = 2; //<==
		while(empty($posts)){
			$posts = $this->Post->find(
				"all",
				array(/*...*/)
			);
			if($counter>5){ //<==
				break; //<==
			}else{ //<==
				$counter += 2; //<==
			} //<==
			if(empty($posts)){
				sleep(2); // sleep for two seconds
			}
		}
		$this->set("posts", $posts);
		$this->render("get_new", '');
	}
</pre>
</p>
<p>
Why is it incremented by two? It's because there's 2 seconds sleep, so I'm checking if the script slept for at least 5 seconds altogether (5 seconds is the timeout of ajax request).</p>
<p>
To finish - call extracted method in success callback:</p>
<pre name="code" class="javascript">
//webroot/js/thread.js
Thread.prototype.update = function(){
	if(!this.updateEndpoint || !this.rootId || !this.lastId){
		alert("Thread corrupted");
		return false;
	}
	_this = this;
	$.ajax({
		url: this.updateEndpoint +"/"+ this.rootId+ "/" + this.lastId,
		dataType: 'json',
		timeout: 5000,
		success: function(data){ //<==
			$(data).each(function(k,v){
				_this.addPost(v.Post.id, v.Post.parent_id, v.Post.message); //<==
			});
			_this.update();
		},
		error: function(){
			_this.update();
		}
	});
	return true;
}
</pre>
</p>
<p>
That's all. Of course with longPooling (and soon with web sockets) you can implement some more cool stuff. You can display information about somebody typing a reply right now in some particular place. </p>
<p>
There are some details to be polished, maybe newly added post should attract attention (blink?), but it's not the case of this tutorial.</p>
<p>
I hope you enjoyed it, please let me know what you think about it.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/10/long-polling-tutorial-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Long Polling, tutorial – cześć 1</title>
		<link>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-czesc-1/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-czesc-1/#comments</comments>
		<pubDate>Mon, 20 Sep 2010 17:30:44 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[JS and friends]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1222</guid>
		<description><![CDATA[(poprzednia część tego tutoriala) Teraz, kiedy mamy wygenerowane drzewo dyskusji, następnym krokiem będzie umożliwienie odpowiedzi w wątku. Użytkownik powinien móc kliknąć pod konkretną wypowiedzią i natychmiast zacząć wpisywać odpowiedź. Dlatego pod każdym elementem li.post utwórzmy div&#8217;a, którym będzie konterem dla &#8230; <a href="http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-czesc-1/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="/?p=1189">(poprzednia część tego tutoriala)</a><br />
<!--pagetitle:Placeholder dla formularza odpowiedzi--><br />
Teraz, kiedy mamy wygenerowane drzewo dyskusji, następnym krokiem będzie umożliwienie odpowiedzi w wątku. Użytkownik powinien móc kliknąć pod konkretną wypowiedzią i natychmiast zacząć wpisywać odpowiedź.<br />
<span id="more-1222"></span></p>
<p>Dlatego pod każdym elementem li.post utwórzmy div&#8217;a, którym będzie konterem dla formularza odpowiedzi. Znajdź w thread.js linie odpowiedzialne za tworzenie elementów li zawierających posty i dodaj html z div&#8217;em do parametru innerHTML:</p>
<pre name="code" class="javascript">
//app/webroot/js/thread.js
$("
<li>", {
	    	  id: v.Post.id,
	    	  innerHTML: "
<div>"+v.Post.message+"</div>

" +
	    	  			 "
<div class='reply' />" //<---
	    	  ,
	 class: "post child"
	 }).
	 appendto("#"+v.post.parent_id+">ul");
</pre>
<p>Przyda się teraz nieco css&#8217;ów. Kontener powinien zajmować nieco więcej miejsca (żeby łatwiej było w  niego kliknąć) oraz po najechaniu na niego myszką powinien zmienić kolor, sugerując użytkownikowi, że może tam kliknąć. Podlinkuj nowy plik css do layoutu:</p>
<pre name="code" class="php">
//app/views/layouts/default.ctp
echo $this->Html->css('cake.generic');
echo $this->Html->css('thread'); //<-add this
echo $this->Javascript->link(array("thread"));
</pre>
<p>a następnie stwórz plik thread.css z taką zawartością:</p>
<pre name="code" class="css">
/* app/webroot/css/thread.css */
div.reply {
	height: 5px;
	cursor: text;
}
div.reply:hover{
	background-color: #8EAFEB;
}
</pre>
<p>
Tak to powinno wyglądać teraz:<br />
<img src="http://blog.grzegorzpawlik.com/wp-content/uploads/2010/09/replyplaceholder.png" alt="" title="replyplaceholder" width="708" height="274" class="alignnone size-full wp-image-1211" />
</p>
<p>Bardziej spostrzegawczy zauważyli od razu, że miejsce pod pierwszą wypowiedzią w wątku nie zachowuje się tak jak powinno, poprawmy nieco thread.js:</p>
<pre name="code" class="javascript">
if($("#"+v.Post.parent_id).find("ul").length == 0){
	if($("#"+v.Post.parent_id).find("div.reply").length == 0){
		$("
<div>", {class: "reply"}).appendTo("#"+v.Post.parent_id);
	}
	$("
<ul>", {class: "posts children"}).appendTo("#"+v.Post.parent_id);
}
</pre>
<p>Teraz pojawiło nam się odrobinę redundancji w kodzie &#8211; dwa miejsca, gdzie jest tworzony element div.reply. Naprawmy to. Usuniemy dodawanie stringa</p>
<pre>
&lt;div class='reply'/&gt;
</pre>
<p>i zastąpimy go tworzeniem elementu DOM przy pomocy jQuery, a następnie wyekstrahujemy motodę, która to robi. Ostatecznie kod powinien wyglądać tak:</p>
<pre name="code" class="javascript">
(function(window, document, udefined){
	window.Thread = function(_posts) {
		this.posts = _posts;
		return this;
	};
})(window, document);

Thread.prototype.createReplyDiv = function(){
	return $("
<div>", {class: "reply"});
}

Thread.prototype.createThread = function(){
	if(this.posts.length <1){
		return this;
	}
	var _this = this; //* patrz poniżej
	$(this.posts).each(function(k, v){
		if($("#"+v.Post.parent_id).find("ul").length == 0){
			if($("#"+v.Post.parent_id).find("div.reply").length == 0){
				_this.createReplyDiv().appendTo("#"+v.Post.parent_id);
			}
	        $("
<ul>", {class: "posts children"}).appendTo("#"+v.Post.parent_id);
		}
	     $("
<li>", {
	    	  id: v.Post.id,
	    	  innerHTML: "
<div>"+v.Post.message+"</div>

"
	    	  ,
	    	  class: "post child"
	     }).
	     append(_this.createReplyDiv()).
	     appendTo("#"+v.Post.parent_id+">ul");
	});
	return this;
}
</pre>
<p>(*)Jeśli zastanawiasz się po co zapamiętujemy `this` w zmiennej `_this` śpieszę z wyjaśnieniem. W kodzie chcemy wywołać, wyodrębnioną dopiero co, metodę createReplyDiv. Niestety w anonimowej funkcji, która jest przekazana do metody each(), `this` wskazuje już na konkretny element z kolekcji po której ów each() iteruje. Musimy zapamiętać &#8220;starą&#8221; referencję `this` (czyli obiekt klasy Thread).</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-czesc-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Long Polling &#8211; tutorial, part 0</title>
		<link>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-part-0/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-part-0/#comments</comments>
		<pubDate>Fri, 10 Sep 2010 17:00:31 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1158</guid>
		<description><![CDATA[I was encouraged by good response on my first tutorial, so started to work on another. Unfortunately I didn&#8217;t finish it before my vacation, so I almost lost track of what I was doing. So I decided to ship it &#8230; <a href="http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-part-0/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<blockquote><p>I was encouraged by good response on <a href="/?p=815">my first tutorial</a>, so started to work on another. Unfortunately I didn&#8217;t finish it before my vacation, so I almost lost track of what I was doing. So I decided to ship it to you in parts, so by doing every part before publishing I&#8217;ll be back on track, and find more mistakes, so hopefully it will be error-free. Enjoy. </p></blockquote>
<p><!--pagetitle:idea and basic preparations--></p>
<h4>What&#8217;s long pooling</h4>
<p>Long pooling is one of the method that mimics &#8220;<a href="http://en.wikipedia.org/wiki/Push_technology">server push</a>&#8221; behavior on stateless HTTP protocol (which is based on &#8220;server pull&#8221; principle) . Even though there are <a href="http://hacks.mozilla.org/2010/04/websockets-in-firefox/">web sockets</a> coming, I think it&#8217;s a good idea to get familiar with that kind of technique. </p>
<p>We&#8217;ll try to create just a bit of simple idea. Combination of chat and bulletin board, where You can post Your messages and can see other users messages appearing in &#8220;real time&#8221;.</p>
<p>We&#8217;ll do it with <a href="http://cakephp.org/">cakePHP</a>  on the server side and http://jquery.com/ on the client side. I assume You are a little bit familiar with that tools &#8211; I wouldn&#8217;t explain how to configure database connection in cakePHP etc.</p>
<p>The only features on this system are</p>
<ul>
<li>creating new discussion</li>
<li>replying in a discussion without reloading a page</li>
<li>getting users to see other people replies without reloading the page</li>
</ul>
<p>on top of that we&#8217;ll keep in mind that we are on small budget, so we want our application to be as light for the server as possible (to do that we&#8217;ll push as much work to client side as we can).</p>
<h4>Preparing project</h4>
<p>Let&#8217;s get last stable version of cake to get started fast:</p>
<pre>
$ git clone http://github.com/cakephp/cakephp.git longPolling
</pre>
<p>Then let&#8217;s create a database for our discussions</p>
<pre name="code" class="sql">
CREATE TABLE `posts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `lft` int(11) DEFAULT NULL,
  `rght` int(11) DEFAULT NULL,
  `message` text COLLATE utf8_unicode_ci NOT NULL,
  `modified` datetime NOT NULL,
  UNIQUE KEY `id` (`id`),
  KEY `parent_id` (`parent_id`),
  KEY `lft` (`lft`),
  KEY `rght` (`rght`),
  KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
</pre>
<p>As You can see, we&#8217;ll use &#8220;<a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data.html">Nested set model</a>&#8221; which is implemented by <a href="http://book.cakephp.org/view/91/Tree">Treebehavior</a>. For maximum simplicity discussions don&#8217;t have topics and so, and to start discussion one should just post first message outside of any discussion.</p>
<p>Let&#8217;s bake model, controller and views for CRUD &#8211; this will be a foundation we&#8217;ll be working on. Just don&#8217;t mind validation and associations, neither admin routing &#8211; it&#8217;s not crucial to the topic. Add Tree behavior to Post model.</p>
<h4>Cleanup</h4>
<p>Now, we have some mess in our code already. `add` and `edit` views duplicate themselves, so I&#8217;ll remove `add` view and force PostsController::add() to render `edit` view:</p>
<pre name="code" class="php">
//posts_controller.php
	function add() {
		if (!empty($this->data)) {
			$this->Post->create();
			if ($this->Post->save($this->data)) {
				$this->Session->setFlash(__('The post has been saved', true));
				$this->redirect(array('action' => 'index'));
			} else {
				$this->Session->setFlash(__('The post could not be saved. Please, try again.', true));
			}
		}
		$this->render("edit");
	}
</pre>
<p>and, just to make it not look silly &#8211; surround code that generates &#8220;delete&#8221; link with one if statement:</p>
<pre name="code" class="php">
		&lt;?php if(!empty($this->data["Post"]["id"])): ?&gt;
			&lt;li><?php echo $this->Html->link(__('Delete', true), array('action' => 'delete', $this->Form->value('Post.id')), null, sprintf(__('Are you sure you want to delete # %s?', true), $this->Form->value('Post.id'))); ?&gt;</li>

		&lt;?php endif; ?>
</pre>
<p>To finally polish it &#8211; remove user_id, lft, rght, modified fields from the form (because there will be automatically filled).</p>
<p>Oh! I almost forgot, to be able to pick some of the existing topics as parent for message you are adding &#8211; create that method in PostsController and call it in add and edit actions:</p>
<pre name="code" class="php">
//posts_controller.php
function _getParentPosts(){
	$this->set('parents', $this->Post->find("list"));
}
</pre>
<p>Now create at least one discussion, so we can start working on some stuff. If it&#8217;s not fun for You just run this query:</p>
<pre name="code" class="sql">
INSERT INTO `posts` VALUES (1,NULL,NULL,1,8,'first message in the topic.','0000-00-00 00:00:00'),
(2,1,NULL,2,5,'my very first reply to the very first message!','0000-00-00 00:00:00'),
(3,1,NULL,6,7,'I just have some opinion about first message, and will share it with you... soon.','0000-00-00 00:00:00'),
(4,2,NULL,3,4,'I think you shouldn\'t make off-topics here','0000-00-00 00:00:00');
</pre>
<p>Now we&#8217;re up and running, and ready for the fun stuff.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/09/long-polling-tutorial-part-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Możesz podzielić swój kod na moduły w cakePHP</title>
		<link>http://blog.grzegorzpawlik.com/2010/07/mozesz-podzielic-swoj-kod-na-moduly-w-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/07/mozesz-podzielic-swoj-kod-na-moduly-w-cakephp/#comments</comments>
		<pubDate>Mon, 19 Jul 2010 11:10:33 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[CakePHP 1.3]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=1078</guid>
		<description><![CDATA[Szybki trick dla dużych projektów: używaj podkatalogów. Pisząc rozbudowaną aplikację możesz podzielić kod na moduły: controllers/module/module_things_controller.php class ModuleThingsController extends AppController { } models/module/module_thing.php class ModuleThing extends AppModel{ } (Moduły uznałbym za coś pośredniego między kupą kontrolerów, a pluginami. Nie wymagają &#8230; <a href="http://blog.grzegorzpawlik.com/2010/07/mozesz-podzielic-swoj-kod-na-moduly-w-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Szybki trick dla dużych projektów: <strong>używaj podkatalogów</strong>.<br />
<span id="more-1078"></span><br />
Pisząc rozbudowaną aplikację możesz podzielić kod na moduły:</p>
<pre name="code" class="php">
controllers/module/module_things_controller.php
class ModuleThingsController extends AppController {
}
models/module/module_thing.php
class ModuleThing extends AppModel{
}
</pre>
<p>(Moduły uznałbym za coś pośredniego między kupą kontrolerów, a pluginami. Nie wymagają mocniejszego rozdzielenia zależności między kontrolerami w przeciwieństwie do pluginów, dla których jest to dobrą praktyką. Z drugiej strony <strong>wprowadzają nieco porządku w plikach źródłowych</strong>)</p>
<p>Od razu rzuca się w oczy fakt powtarzania nazwy modułu w katalogu i nazwie pliku (oraz w definicji klasy). Wynika to po pierwsze z konieczności jednoznacznego nazywania klas w ramach jednego projektu. Po drugie, jeśli istnieją dwa pliki od tej samej nazwie i definicji klasy (ale jeden w podkatalogu) to domyślnie będzie używany ten z katalogu głównego.</p>
<p>Tak czy siak warto wprowadzić nieco porządku do swoich projektów. Można to zrobić nawet z marszu &#8211; po prostu przenieść pliki do podkatalogów (jednak należy pamiętać o tym co może się wydarzać, gdy przez przypadek zdefiniujemy dwa kontrolery o takiej samej nazwie w różnych podkatalogach.)</p>
<p>Niestety w widokach nie udało mi się jeszcze w ten sposób wykorzystać podkatalogów. Dlatego w naszym przykładzie widoki należy umieścić w </p>
<pre>
/views/module_things/
</pre>
<p>Jednak już w ramach tego katalogu można zadbać o większy porządek:<br />
/views/module_things/forms/add_edit_form.ctp<br />
i explicite wywoływać dany widok:</p>
<pre name="code" class="php">
class ModuleThingsController extends AppController {
 //...
 function add(){
   //...
   $this->render("forms/add_edit_form");
 }
}
</pre>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/07/mozesz-podzielic-swoj-kod-na-moduly-w-cakephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Często podmieniasz fragmenty stringów (cakePHP)?</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/czesto-podmieniasz-fragmenty-stringow-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/czesto-podmieniasz-fragmenty-stringow-cakephp/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 17:53:40 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=855</guid>
		<description><![CDATA[Jeśli tak &#8211; mam dla Ciebie małą podpowiedź String::insert() Przydatne, jeśli potrzebujesz zdefiniowany łańcuch w którym podmieniasz tylko niektóre fragmenty: $wiadomosc = ":name ma :pet"; echo String::insert( $wiadomosc, array( "name"=>"Ala", "pet" => "Kota" ) ); // Ala ma Kota Z &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/czesto-podmieniasz-fragmenty-stringow-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Jeśli tak &#8211; mam dla Ciebie małą podpowiedź <a href="http://api.cakephp.org/class/string#method-Stringinsert">String::insert()</a></p>
<p>Przydatne, jeśli potrzebujesz zdefiniowany łańcuch w którym podmieniasz tylko niektóre fragmenty:</p>
<pre name="code" class="php">
$wiadomosc = ":name ma :pet";
echo String::insert(
   $wiadomosc,
   array(
      "name"=>"Ala",
      "pet" => "Kota"
   )
); // Ala ma Kota
</pre>
<p>Z pewnością uprości to Twój kod, jeśli znikną z niego wszystkie <a href="http://pl2.php.net/str_replace">str_replace</a> itp.</p>
<p>Polecam też resztę klasy String i innych w cake/lib &#8211; często okazują się przydatne.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/czesto-podmieniasz-fragmenty-stringow-cakephp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>cakephp comments</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments-2/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments-2/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 18:31:57 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[comment]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=840</guid>
		<description><![CDATA[Google analytics tells me that I have a lot of gust on &#8220;cakephp comments&#8221; search phrase. They&#8217;re not happy because I have nothing to offer in this matter yet. The bounce rate on that phrase is above 99%. I don&#8217;t &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/cakephp-comments-2/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Google analytics tells me that I have a lot of gust on &#8220;cakephp comments&#8221; search phrase. They&#8217;re not happy because I have nothing to offer in this matter yet. The bounce rate on that phrase is above 99%.</p>
<p>I don&#8217;t mind that, but don&#8217;t want to leave You without any help. So I&#8217;ll leave a road mark for You:<br />
<a href="http://cakedc.com/eng/graham_weldon/2010/03/10/feature-rich-customizable-comments-plugin">Feature rich, customizable comments plugin</a></p>
<p>When You have this pluggin up and running in Your app &#8211; drop in back to me and read about how to make <a href="/?p=815">cool web 2.0 dashboard view and learn some of jQuery features</a>.</p>
<p>Good luck!</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>cakephp comments</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 18:30:37 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[comments]]></category>
		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=836</guid>
		<description><![CDATA[Mam wielu gości, którzy wpadają na tego bloga przez frazę &#8220;cakephp comments&#8221;. Niestety lądują tu przez przypadek &#8211; piszę o cakephp i na końcu każdego postu są komentarze ;) Nic interesującego i potwierdza to bounce rate powyżej 99% :D Jednak &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/cakephp-comments/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Mam wielu gości, którzy wpadają na tego bloga przez frazę &#8220;cakephp comments&#8221;. Niestety lądują tu przez przypadek &#8211; piszę o cakephp i na końcu każdego postu są komentarze ;) Nic interesującego i potwierdza to bounce rate powyżej 99% :D</p>
<p>Jednak mimo, że osobiście nie mam nic do zaoferowania w tej kwestii &#8211; mogę pozostawić drogowskaz dla wszystkich poszukujących:<br />
<a href="http://cakedc.com/eng/graham_weldon/2010/03/10/feature-rich-customizable-comments-plugin">Plugin komentarzy cakePHP</a></p>
<p>Jak już podepniecie ten mechanizm w swoich systemach &#8211; wpadnijcie znów do mnie i poczytajcie o tym jak <a href="/?p=706">stworzyć fajny widok zbiorczy web 2.0 i poznać przy okazji możliwości jQuery</a>.</p>
<p>Powodzenia!</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/cakephp-comments/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to merge edit() and add() methods in cakephp&#8230;</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/how-to-merge-edit-and-add-methods-in-cakephp/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/how-to-merge-edit-and-add-methods-in-cakephp/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 05:00:35 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[DRY]]></category>
		<category><![CDATA[refactoring]]></category>
		<category><![CDATA[View]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=825</guid>
		<description><![CDATA[When You get a closer look at baked controllers You can get the idea that they&#8217;re breaking DRY principle. Can You tell which of two methods in baked controller are almost identical? If not &#8211; check out which two views &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/how-to-merge-edit-and-add-methods-in-cakephp/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>When You get a closer look at <a href="http://book.cakephp.org/view/113/Code-Generation-with-Bake">baked controllers</a> You can get the idea that they&#8217;re breaking <a href="http://en.wikipedia.org/wiki/DRY">DRY principle</a>.</p>
<p>Can You tell which of two methods in baked controller are almost identical? If not &#8211; check out which two views are even more similar. It&#8217;s not much of a riddle if You read title of this post again, though. That&#8217;s right.</p>
<p>Lets look at views first (in this example we have Thing model wit one field called &#8216;name&#8217;):</p>
<pre name="code" class="php">
// app/views/things/add.ctp
&lt;div class="things form"&gt;
&lt;?php echo $this-&gt;Form-&gt;create('Thing');?&gt;
   &lt;fieldset&gt;
      &lt;legend&gt;
         &lt;?php printf(__('Add %s', true), __('Thing', true)); ?&gt;
      &lt;/legend&gt;
   &lt;?php
      echo $this-&gt;Form-&gt;input('name');
   ?&gt;
      &lt;/fieldset&gt;
&lt;?php echo $this-&gt;Form-&gt;end(__('Submit', true));?&gt;
&lt;/div&gt;
&lt;div class="actions"&gt;
   &lt;h3&gt;&lt;?php __('Actions'); ?&gt;&lt;/h3&gt;
   &lt;ul&gt;
      &lt;li&gt;
         &lt;?php echo $this-&gt;Html-&gt;link(
            sprintf(
               __('List %s', true),
               __('Things', true)
            ),
            array('action' =&gt; 'index')
         );?&gt;
      &lt;/li&gt;
   &lt;/ul&gt;
&lt;/div&gt;
</pre>
<pre name="code" class="php">
// app/views/things/edit.ctp
&lt;div class="things form"&gt;
&lt;?php echo $this-&gt;Form-&gt;create('Thing');?&gt;
   &lt;fieldset&gt;
      &lt;legend&gt;
         &lt;?php printf(__('Edit %s', true), __('Thing', true)); ?&gt;
      &lt;/legend&gt;
   &lt;?php
      echo $this-&gt;Form-&gt;input('id');
      echo $this-&gt;Form-&gt;input('name');
   ?&gt;
   &lt;/fieldset&gt;
&lt;?php echo $this-&gt;Form-&gt;end(__('Submit', true));?&gt;
&lt;/div&gt;
&lt;div class="actions"&gt;
   &lt;h3&gt;&lt;?php __('Actions'); ?&gt;&lt;/h3&gt;
   &lt;ul&gt;
      &lt;li&gt;
         &lt;?php echo $this-&gt;Html-&gt;link(
            __('Delete', true),
            array('action' =&gt; 'delete', $this-&gt;Form-&gt;value('Thing.id')),
            null,
            sprintf(
               __('Are you sure you want to delete # %s?', true),
               $this-&gt;Form-&gt;value('Thing.id')
            )
         ); ?&gt;
      &lt;/li&gt;
      &lt;li&gt;
         &lt;?php echo $this-&gt;Html-&gt;link(
            sprintf(__('List %s', true), __('Things', true)),
            array('action' =&gt; 'index')
         );?&gt;
      &lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</pre>
<p>They differ in 3 line (of 16). If there&#8217;s a change in model (ie. adding one field) &#8211; It&#8217;s needed to edit two files. That&#8217;s stupid.<br />
Lets take a look at methods:</p>
<pre name="code" class="php">
// app/controllers/things_controller.ctp
function add() {
   if (!empty($this->data)) {
      $this->Thing->create();
      if ($this->Thing->save($this->data)) {
         $this->Session->setFlash(__('The thing has been saved', true));
         $this->redirect(array('action' => 'index'));
      } else {
         $this->Session->setFlash(
            __('The thing could not be saved. Please, try again.', true)
         );
      }
   }
}

function edit($id = null) {
   if (!$id &#038;&#038; empty($this->data)) {
      $this->Session->setFlash(__('Invalid thing', true));
      $this->redirect(array('action' => 'index'));
   }
   if (!empty($this->data)) {
      if ($this->Thing->save($this->data)) {
         $this->Session->setFlash(__('The thing has been saved', true));
         $this->redirect(array('action' => 'index'));
      } else {
         $this->Session->setFlash(
            __('The thing could not be saved. Please, try again.', true)
         );
      }
   }
   if (empty($this->data)) {
      $this->data = $this->Thing->read(null, $id);
   }
}
</pre>
<p>It&#8217;s clear that edit() metdod encloses add() method body. There would be no much change if You just paste add() method into second if statement, wouldn&#8217;t it?</p>
<p>We&#8217;ll fix it with few tricks. We need to know how $this->Thing->create() exactly works. It forces adding of a new element into DB (because save() method works as &#8216;update&#8217; or &#8216;create&#8217; and it depends on current model state). But if You don&#8217;t pass any parameters to create() method the only thing it does is cleaning $this->data and $this->id (&#8216;this&#8217; means model Thing in current context).<br />
There&#8217;s no way that $this->Thing->data contains any junk while executed, so it&#8217;s safe to drop this line &#8211; it will still work (is it?! &#8211; <a href="#security">spoiler</a>).<br />
Now whole method body is identical with code block in edit method. Lets drop add() method and it&#8217;s view. Alter old links pointing at /things/add to /things/edit/ (without param).<br />
Lets try out how does it work right now?</p>
<p>Based on version &#8211; 1.2 will crush, 1.3 shows &#8220;Invalid thing&#8221; alert. I&#8217;ll focus on 1.3 version (as far as I remember changing method signature from edit($id) to edit($id=null) should get You back on track in 1.2).</p>
<p>From now on we can assume that when there&#8217;s no id in url &#8211; it means that someone want to add new Thing, so it&#8217;s ok to delete this if statement:</p>
<pre name="code" class="php">
// app/controllers/things_controller.php
		if (!$id &#038;&#038; empty($this->data)) {
			$this->Session->setFlash(__('Invalid thing', true));
			$this->redirect(array('action' => 'index'));
		}
</pre>
<p>Now we can add new Things through edit() form- everything works ok now.<br />
It&#8217;s good to polish this view, though. &#8220;edit&#8221; header look silly while adding new Thing&#8230; so delete button.</p>
<p>pimp up Your legend tag:</p>
<pre name="code" class="php">
// app/views/things/edit.ctp
 		&lt;legend&gt;
 		   &lt;?php printf(
 		      (!is_null($this-&gt;data["Thing"]["id"])?__('Edit %s', true): __('Add %s', true)),
 		      __('Thing', true));
 		   ?&gt;
 		 &lt;/legend&gt;
</pre>
<p>and surround first li element in if block:</p>
<pre name="code" class="php">
// app/views/things/edit.ctp
&lt;?php if(!is_null($this-&gt;data["Thing"]["id"])): ?&gt;
   &lt;li&gt;
      &lt;?php echo $this-&gt;Html-&gt;link(
         __('Delete', true),
         array('action' =&gt; 'delete', $this-&gt;Form-&gt;value('Thing.id')),
         null,
         sprintf(
            __('Are you sure you want to delete # %s?', true),
            $this-&gt;Form-&gt;value('Thing.id')
         )
      ); ?&gt;
   &lt;/li&gt;
&lt;?php endif; ?&gt;
</pre>
<p>That&#8217;s it.</p>
<p><strong id="security"><br />
Removing $this->Thing->create() not so secure?</strong><br />
I said that after deleting this line nothing bad will happen.<br />
It&#8217;s true, but only if You make all the changes explained in this post. If You just remove $this->Thing->create() You&#8217;ll get an security issue. Someone could send prepared post request to this address with field named &#8220;data[Thing][id]&#8221; and edit an element through add action.<br />
If you heavily depends on ACL authorisation, and one group of users can add Things, and other can edit them &#8211; member of the first group can raise his privileges by well prepared post request.<br />
I think it&#8217;s rather rare situation. If You are interested in fixing this &#8211; please comment this post.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/how-to-merge-edit-and-add-methods-in-cakephp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

