<?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; View</title>
	<atom:link href="http://blog.grzegorzpawlik.com/tag/view/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>Dług techniczny &#8211; przykład z życia wzięty</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/dlug-techniczny-przyklad-z-zycia-wziety/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/dlug-techniczny-przyklad-z-zycia-wziety/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 11:24:22 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[DRY]]></category>
		<category><![CDATA[dług techniczny]]></category>
		<category><![CDATA[element]]></category>
		<category><![CDATA[refactoring]]></category>
		<category><![CDATA[View]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=867</guid>
		<description><![CDATA[Można się spierać co jest długiem technicznym, a co nie jest. Chciałbym Wam pokazać przykład kodu, który świetnie nadaje się do zobrazowania problemu. Problem: Na jeden ze stron jest wyszukiwarka- formularz, w którym po wpisaniu danych w inputach i selectach &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/dlug-techniczny-przyklad-z-zycia-wziety/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Można się spierać co jest długiem technicznym, a co nie jest. Chciałbym Wam pokazać przykład kodu, który świetnie nadaje się do zobrazowania problemu.</p>
<p><strong>Problem:</strong><br />
Na jeden ze stron jest wyszukiwarka- formularz, w którym po wpisaniu danych w inputach i selectach przesyła te dane jako parametry &#8220;named&#8221;, np.:<br />
example.com/controller/action/field1:value1/field2:value2<br />
Potrzebny jest kawałek kodu javascript, który wygeneruje odpowiedni link i wywoła ten adres</p>
<p><strong>Rozwiązanie 1:</strong></p>
<pre name="code" class="javascript">
function submitform()
{
    form = document.getElementById('car_form');
    name = form.elements["CarName"].value;
    registration_number = form.elements["CarRegistrationNumber"].value;
    entry = document.getElementById('CarEntry').value
    driver = document.getElementById('CarDriver').value
    linkForm = '<?php echo $html->url('/cars/find/')?>';
    location = linkForm+'name:'+name+'/registration_number:'+registration_number+'/entry:'+entry+'/driver:'+driver;
    document.forms["car_form"].action = location;
}
</pre>
<p>Jest ono poprawne. Warto się zastanowić, czy został w tym przypadku zaciągnięty dług techniczny? Na pewno będą tacy,  którzy od razu będą wiedzieć co z tym kodem jest nie tak. Inni będą to czuli w kościach. Resztę pocieszę mówiąc, że ta umiejętność przychodzi z czasem pod warunkiem, że chcesz się uczyć jak pisać dobry kod.</p>
<p>Ten formularz wisi sobie gdzieś w jakimś panelu admina w listingu samochodów. Jednak taka wyszukiwarka jest potrzebna też przy listingu kierowców. Oczywiście kopiujesz rozwiązanie i poprawiasz je do nowych warunków. Nie ma w tym nic złego pod warunkiem, że robisz to z odpowiednim nastawieniem: &#8220;kopiuję kod, żeby móc zauważyć części wspólne i przeprowadzić refactoring&#8221; (czasem łatwiej mieć dwa dublujące się elementy i na ich podstawie tworzyć uniwersalny element/funkcje niż wymyślać ją od zera). </p>
<p>No i zabierając się za refactoring &#8211; w tym wypadku chciałbym zbudować wspólny element (dla tych co raczej siedzą w RoR &#8211; partial), który wywołam sobie tam, gdzie potrzebuję &#8211; zaczynam widzieć, że teraz oto przyjdzie mi spłacić ów mistyczny dług techniczny. Na czym on polega? Otóż w moim przykładzie funkcja budująca url jest strasznie &#8220;sztywna&#8221;. Działa w tym i tylko w tym przypadku. Nie ma najmniejszych znamion uniwersalności. Dlatego teraz, zanim zacznę myśleć o elemencie, muszę tą funkcję przebudować na bardziej uniwersalną. Co to znaczy?</p>
<p>Powinna być w stanie złapać wszystkie inputy i selecty, które są istotne przy budowaniu url&#8217;a i iterując po nich sprytnie go zbudować. Mogła by wyglądać na przykład tak:</p>
<p><strong>Rowziązanie 2:</strong></p>
<pre name="code" class="javascript">
function submitform(){
    var params = "";

    $("#car_form input[type!=submit][name!=_method], #car_form select").each(
    	    function(index,element) {
        	    params +=
        	    		  element.name.substr(
        	    				  element.name.lastIndexOf("[")
        	    		  ).slice(1, -1)+
        	    		  ":"+
        	    		  element.value+
        	    		  "/";
    	    }
    );
    window.location.href = '<?php echo $html->url('/cars/find/')?>' + params;
    return false;
}
</pre>
<p>Teraz inputy i selecty są zbierane bardziej automatycznie ("#car_form input[type!=submit][name!=_method], #car_form select"), a nazwy parametrów są brane z parametru name tych inputów i selectów. Dzięki temu powinna działać dla każdego formularza. Mniejsza o szczegóły.</p>
<p>Jednak rozwiązanie 1 nie było złe. Było wręcz idealne (bo proste i zrozumiałe) tak długo, jak było potrzebne w jednym miejscu. Rozwiązanie 2 mogło zająć przynajmniej 4 razy tyle czasu co pierwsze. Dlatego dopóki nie było powtórzeń kodu - było dobre. Dług techniczny nie istniał. Jednak pojawił się w momencie, kiedy zacząłem refaktoring kodu. </p>
<p>Jak to możliwe, że dług techniczny pojawił się w momencie, kiedy zacząłem go "spłacać"?<br />
Czy istniał cały czas i był ukryty, a ja go nagle odkryłem?</p>
<p>Odpowiem na to pytanie nie wprost: Dług techniczny jest tylko pojęciem umożliwiającym zrozumienie ludziom, kŧórzy nie są programistami, dlaczego nie wolno im zachęcać programistów do chodzenia na skróty. Uzmysławia też (mam nadzieję) niedoświadczonym programistom jakie są niebezpieczeństwa związane z chodzeniem na skróty.</p>
<p><del datetime="2010-03-20T10:48:45+00:00">Leniwy</del> <a href="/?p=29">Dobry</a>, doświadczony programista w ogóle nie musi zagłębiać się w szczegóły czy dług techniczny istnieje, czy nie. <strong>Po prostu tworzy dobry kod</strong>.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/dlug-techniczny-przyklad-z-zycia-wziety/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Code snippet: ekstrakcja nazwy pola z inputów cakePHP przy pomocy jQuery</title>
		<link>http://blog.grzegorzpawlik.com/2010/03/code-snippet-ekstrakcja-nazwy-pola-z-inputow-cakephp-przy-pomocy-jquery/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/03/code-snippet-ekstrakcja-nazwy-pola-z-inputow-cakephp-przy-pomocy-jquery/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 16:00:27 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[code-snippet]]></category>
		<category><![CDATA[FormHelper]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[View]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=860</guid>
		<description><![CDATA[Natknąłem się na następujący problem: Mam formularz wyszukiwania, który wygląda różnie w różnych widokach, jednak na jego podstawie jest budowany url tak samo dla każdego widoku. Na przykład: // some view echo $form->create("Model"); echo $form->input("field1"); echo $form->input("field2"); echo $form->input("field3"); echo &#8230; <a href="http://blog.grzegorzpawlik.com/2010/03/code-snippet-ekstrakcja-nazwy-pola-z-inputow-cakephp-przy-pomocy-jquery/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Natknąłem się na następujący problem:</p>
<p>Mam formularz wyszukiwania, który wygląda różnie w różnych widokach, jednak na jego podstawie jest budowany url tak samo dla każdego widoku. Na przykład:</p>
<pre name="code" class="php">
// some view
echo $form->create("Model");
echo $form->input("field1");
echo $form->input("field2");
echo $form->input("field3");
echo $form->end("submit");
</pre>
<p>W zdarzeniu onsubmit chcę wygenerować taki url:<br />
&#8220;/field1:value1/field2:value2/field3:value3&#8243;<br />
Gdzie valueX to oczywiście wartość danego pola.</p>
<p>Pobranie interesujących nas pól:</p>
<pre name="code" class="javascript">
$("form input[type!=submit][name!=_method], form select");
</pre>
<p>Tak można wytargać nazwę pola z &#8220;name&#8221; inputów (zakładając, że w zmiennej string mamy cały name):<br />
<del datetime="2010-03-18T14:20:44+00:00">
<pre name="code" class="javascript">
// string.substr(
//   string.indexOf("[", string.indexOf("]"))
// ).slice(1, -1);
// nowa, lepsza, prostsza wersja:
string.substr(
   string.lastIndexOf("[")
).slice(1, -1)
</pre>
<p></del><br />
Całość tworzenia linków:</p>
<pre name="code" class="javascript">
var params = "";

$("form input[type!=submit][name!=_method], form select").each(
   function(index,element) {
      params +=
         element.name.substr(
            element.name.lastIndexOf("[")
         ).slice(1, -1)+
         ":"+
         element.value+
         "/";
   }
);
</pre>
<p>Przydaje się, jeśli pracujesz z parametrami &#8220;named&#8221;</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/03/code-snippet-ekstrakcja-nazwy-pola-z-inputow-cakephp-przy-pomocy-jquery/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>
		<item>
		<title>Zcalenie edit() i add() w kontrolerze</title>
		<link>http://blog.grzegorzpawlik.com/2010/02/zcalenie-edit-i-add-w-kontrolerze/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/02/zcalenie-edit-i-add-w-kontrolerze/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 14:00:20 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<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=662</guid>
		<description><![CDATA[Gdy przyjrzysz się bliżej kontrolerom stworzonym przy pomocy narzędzia &#8216;bake&#8217; (lub takim, jakie powstają po wykonaniu tutoriala) możesz stwierdzić, że łamią one koncepcję DRY. Jesteś w stanie powiedzieć, które z dwóch metod są niemal identyczne? Jeśli nie &#8211; sprawdź, które &#8230; <a href="http://blog.grzegorzpawlik.com/2010/02/zcalenie-edit-i-add-w-kontrolerze/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Gdy przyjrzysz się bliżej kontrolerom stworzonym przy pomocy narzędzia <a href="http://book.cakephp.org/view/113/Code-Generation-with-Bake">&#8216;bake&#8217;</a> (lub takim, jakie powstają po wykonaniu <a href="http://book.cakephp.org/view/219/Blog">tutoriala</a>) możesz stwierdzić, że łamią one koncepcję <a href="http://pl.wikipedia.org/wiki/DRY">DRY</a>. Jesteś w stanie powiedzieć, które z dwóch metod są niemal identyczne? Jeśli nie &#8211; sprawdź, które dwa widoki są niemal identyczne. Ok, może spaliłem swoją zagadkę, bo w tytule tego postu jest wyraźna sugestia o czym będę mówił. Zgadza się.</p>
<p>Najpierw rzućmy okiem na widoki (tu w przykładzie dla Modelu Thing, który ma jedno pole &#8211; name):</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>Różnią się dokładnie w trzech linijkach (na 16 łącznie). Jeśli zmienisz coś w jednym (na przykład dodasz pole) to będziesz musiał poprawić formularz w dwóch plikach. Czysta głupota.<br />
Przyjrzyjmy się jeszcze metodom:</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>Praktycznie całe ciało metody add() można by wkleić w miejsce drugiego if&#8217;a w metodzie edit() i niewiele by się zmieniło, prawda?</p>
<p>Kilka tricków pozwoli nam naprawić to marnotrawstwo. W zasadzie najważniejsza kwestia jest następująca &#8211; jak działa metoda $this->Thing->create()? Wymusi dodanie nowego elementu (dlatego, że metoda save() działa jako create lub update w zależności od stanu modelu). Ale nie przekazując żadnych parametrów do tej metody tak naprawdę sprowadzi się do wyczyszczenia $this->data i $this->id (this oznacza w tym przypadku model). W przypadku metody add() nie ma możliwości, żeby jakieś śmieci były w $this->Thing->data, więc możemy spokojnie usunąć tą linijkę &#8211; dalej będzie działać tak samo (czy na pewno? &#8211; <a href="#bezpieczenstwo">spoiler</a>).<br />
Teraz całkowicie ciało metody add() mogłoby zawrzeć się w metodzie edit(). Dlatego trzeba ją usunąć (razem z jej widokiem). A wszystkie linki, które prowadziły do /things/add niech teraz prowadzą do /things/edit.<br />
Warto sprawdzić, co się teraz dzieje?</p>
<p>W zależności od wersji &#8211; 1.2 się wywali, 1.3 pokaże informacje &#8220;Invalid thing&#8221;. Teraz skupię się na wersji 1.3 &#8211; bo na niej sprawdzam na bieżąco efekty pisząc ten wpis. (dla 1.2 jeśli dobrze pamiętam wystarczy wstępnie zmienić sygnaturę metody edit($id) na edit($id=null), dalsze kroki będą analogiczne, choć kod w szczegółach może się różnić)</p>
<p>Od teraz możemy założyć, że jeśli w url&#8217;u nie ma podanego id &#8211; to znaczy, że chcemy dodać nowy element, zatem tego if&#8217;a możemy usunąć:</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>Możemy spokojnie teraz dodawać nowe elementy przez formularz- wszystko działa.<br />
Warto jeszcze dopieścić widok &#8211; bez sensu jest nagłówek &#8220;edit&#8221; przy dodawaniu elementu, oraz przycisk &#8220;delete&#8221;,<br />
fragment z legend poprawiamy na taki (dodałem wcięcia dla przejrzystości):</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>a pierwszy element li otaczamy if&#8217;ami tak:</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>I tyle.</p>
<p><strong id="bezpieczenstwo">Usunięcie $this->Thing->create() nie takie bezpieczne?</strong><br />
Napisałem, że po usunięciu tego elementu nic się złego nie stanie. Oczywiście jest to prawdą pod warunkiem, że wykonasz wszystkie czynności opisane w tym poscie. Jeśli tylko usuniesz $this->Thing->create() narażasz się na niebezpieczeństwo, że ktoś wyśle spreparowanego post&#8217;a pod ten adres. Jeśli poda w nim pole o nazwie data[Thing][id] &#8211; będzie mógł zmienić dane dla elementu o danym id. Jeśli masz sytuację taką, że jedna grupa może dodawać elementy, a inna je edytować i polegasz tylko na acl&#8217;ach (dana grupa ma dostęp do edit(), a kolejna do add()) &#8211; to w ten sposób możesz stworzyć lukę. Jednak ta sytuacja jest dość rzadka i to jest raczej temat na inny wpis, więc jeśli interesuje Cię rowiązanie tego problemu &#8211; zachęcam do komentowania.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/02/zcalenie-edit-i-add-w-kontrolerze/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AppView &#8211; wrapper View zupełnie jak AppController?</title>
		<link>http://blog.grzegorzpawlik.com/2009/07/appview-wrapper-view-zupelnie-jak-appcontroller/</link>
		<comments>http://blog.grzegorzpawlik.com/2009/07/appview-wrapper-view-zupelnie-jak-appcontroller/#comments</comments>
		<pubDate>Wed, 22 Jul 2009 12:57:42 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cake]]></category>
		<category><![CDATA[View]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=339</guid>
		<description><![CDATA[Gdy potrzebujesz wrappera dla widoków (takiego jak masz dla kontrolerów i modeli odpowiednio AppController i AppModel), wystarczy kilka prostych kroków: 1. W AppController dodaj var $view = "App"; 2. w /app/views/ utwórz plik App.php, jego zawartość to: class AppView extends &#8230; <a href="http://blog.grzegorzpawlik.com/2009/07/appview-wrapper-view-zupelnie-jak-appcontroller/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Gdy potrzebujesz wrappera dla widoków (takiego jak masz dla kontrolerów i modeli odpowiednio AppController i AppModel), wystarczy kilka prostych kroków:<br />
1. W AppController dodaj<br />
<code lang="php"><br />
var $view = "App";<br />
</code><br />
2. w /app/views/ utwórz plik App.php, jego zawartość to:<br />
<code lang="php"><br />
class AppView extends View {<br />
}<br />
</code></p>
<p>Tyle :D (dzięki Ad7Six na <a href="http://groups.google.com/group/cake-php/browse_thread/thread/3d9e3449718f5114">http://groups.google.com/group/cake-php/browse_thread/thread/3d9e3449718f5114</a>).</p>
<p>Po co to komu? Mnie akurat przydało się, żeby mojemu lenistwu stało się zadość. Nie chciało mi się w widokach pisać długich ścieżek do prototype:<br />
<code lang="php"><br />
echo $javascript->link('/forum/js/scriptaculous-js-1.8.2/lib/prototype', false);<br />
echo $javascript->link('/forum/js/scriptaculous-js-1.8.2/src/scriptaculous', false);<br />
</code></p>
<p>więc utworzyłem AppView:<br />
<code lang="php"><br />
class AppView extends View {<br />
   var $prototype = '/forum/js/scriptaculous-js-1.8.2/lib/prototype';<br />
   var $scriptaculous = '/forum/js/scriptaculous-js-1.8.2/lib/prototype';<br />
}<br />
</code></p>
<p>i teraz załączanie tych bibliotek wygląda tak:<br />
<code lang="php"><br />
echo $javascript->link($this->prototype, false);<br />
echo $javascript->link($this->scriptaculous, false);<br />
</code></p>
<p>Oczywiście w razie, gdybym podmieniał biblioteki na nowsze &#8211; zrobię to tylko w jednym miejscu</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2009/07/appview-wrapper-view-zupelnie-jak-appcontroller/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

