<?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; Controller</title>
	<atom:link href="http://blog.grzegorzpawlik.com/tag/controller/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>Fat model, skinny controller &#8211; przykład</title>
		<link>http://blog.grzegorzpawlik.com/2010/05/fat-model-skinny-controller-przyklad/</link>
		<comments>http://blog.grzegorzpawlik.com/2010/05/fat-model-skinny-controller-przyklad/#comments</comments>
		<pubDate>Fri, 21 May 2010 04:00:00 +0000</pubDate>
		<dc:creator>Greg</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Controller]]></category>
		<category><![CDATA[DRY]]></category>
		<category><![CDATA[model]]></category>

		<guid isPermaLink="false">http://blog.grzegorzpawlik.com/?p=975</guid>
		<description><![CDATA[Niedawno przy projekcie trafiliśmy na ciekawy problem. Chciałbym się podzielić z Wami tym czego się nauczyliśmy. Problem przedstawię w bardzo uproszczonej formie, bo trudno bez machania rękoma przy zabazgranej tablicy wyjaśnić go w całości. Problem Mamy formularz dodawania kosztów, który &#8230; <a href="http://blog.grzegorzpawlik.com/2010/05/fat-model-skinny-controller-przyklad/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Niedawno przy projekcie trafiliśmy na ciekawy problem. Chciałbym się podzielić z Wami tym czego się nauczyliśmy.</p>
<p>Problem przedstawię w bardzo uproszczonej formie, bo trudno bez machania rękoma przy zabazgranej tablicy wyjaśnić go w całości.</p>
<h4>Problem</h4>
<p>Mamy formularz dodawania kosztów, który jest dość specyficzny. Można dodać &#8220;łysy koszt&#8221; i wtedy pojawia się formularz z wyborem firmy i innymi atrybutami. Można jednak z widoku konkretnej firmy wybrać opcję &#8220;dodaj koszt&#8221; &#8211; w takim wypadku lista wyboru firm się nie pojawia.</p>
<p><a href="http://blog.grzegorzpawlik.com/wp-content/uploads/2010/05/Costs_blank.png" rel="lightbox[975]" title="przykładowe formularze"><img src="http://blog.grzegorzpawlik.com/wp-content/uploads/2010/05/Costs_blank.png" alt="" title="przykładowe formularze" width="500" height="123" class="alignnone size-full wp-image-976" /></a></p>
<p>Dość naturalne jest załatwić to kontrolerem. Są różne możliwości, jedną z nich jest takie zdefiniowanie akcji:</p>
<pre name="code" class="php">
function add($entryId=null) {
   if(!empty($this->data)){
       $this->Cost->save($this->data);
       // redirect w jakieś przyjemne okolice
   }
   if(is_null($entryId)){
      $this->set("entry", $this->Cost->Entry->read(null, $entryId);
   }else{
      $this->set("entries", $this->Cost->Entry->find("list");
   }
}
</pre>
<p>W widoku wiadomo &#8211; jeśli jest $entry, wyświetlimy $entry["Entry"]["name"] i w jakimś ukrytym inpucie wrzucimy id, jeśli istnieje $entries &#8211; wyświetlimy selecta.</p>
<p>Na razie w kontrolerze wygląda to wystarczająco zgrabnie, żeby tego nie ruszać. Ale jeśli znajdziesz się w sytuacji, gdy ilość przypadków jest coraz większa &#8211; możesz się zorientować, że dodajesz kolejne argumenty do metody add i rozbudowujesz tą metodę. W takiej sytuacji może Ci pomóc takie podejście.</p>
<h4>Propozycja rozwiązania</h4>
<p>Po pierwsze olej zwykłe parametry funkcji, a zacznij używać parametrów &#8220;named&#8221;. Czyli zamiast:<br />
costs/add/1/3/45/23<br />
costs/add/2///22<br />
będziesz miał<br />
costs/add/cost_id:1/entry_id:3/param3:45/param4:23<br />
costs/add/cost_id:2/param4:22</p>
<p>Po drugie &#8211; przenieś logikę działania do modelu. np:</p>
<pre name="code" class="php">
//model Cost
function giveMeProperData($params){
   if(isset($params["cost_id")){
      // do sth
      return $some_array;
   }
   if(isset($params["entry_id")){
      // do sth
      return $other_array;
   }
   return array();
}
</pre>
<p>Kontroler uprość do granic możliwości:</p>
<pre name="code" class="php">
function add($entryId=null) {
   if(!empty($this->data)){
       $this->Cost->save($this->data);
       // redirect w jakieś przyjemne okolice
   }
   $this->data = $this->Cost->giveMeProperData($this->params["named"];
}
</pre>
<p>A w widoku w zależności od tego w jaki sposób wygląda $this->data renderuj widok.<br />
Jeśli w tablicy jest &#8220;entry_id&#8221; to wyświetl zawartość $this->data["Entry"]["name"], a wartość $this->data["Entry"]["id"] przypisz do jakiegoś ukrytego pola. Itd.</p>
<h4>Co zyskujesz?</h4>
<ol>
<li>Kontroler robi to co do niego należy. Przestaje go interesować przypadek właśnie rozpatrywany. Jego interesuje tylko czy został przesłany formularz. Jeśli tak &#8211; spróbuj zapisać i przekierować, jeśli nie &#8211; pobierz dla niego dane i wyświetl (przekaż do widoku)</li>
<li>Testowanie modeli, choć nie tak proste, jest o niebo łatwiejsze od testowania kontrolerów i widoków &#8211; możesz pokryć ważną część aplikacji testami. Możesz testować, czy metoda giveMeProperData() zwraca to, czego się spodziewasz dla konkretnych argumentów.</li>
<li>Jeśli pokryjesz tą metodę testami będziesz mógł w komfortowych warunkach refaktoryzować ten kod &#8211; jak się pojawią powtórzenia wyodrębnisz wspólne części itd. Gdyby to siedziała w kontrolerze &#8211; nikt nie miałby ochoty tam zajrzeć, a testowanie polegało by głównie na odświeżaniu widoków w przeglądarce dla każdego z przypadków.</li>
<li>Teraz widok jest klasą, która &#8220;ma wiedzę&#8221; na temat tego jak się zachować w zależności od otrzymanych danych (<strong>w prostych przypadkach to już się dzieje</strong> &#8211; chociażby w akcjach &#8220;edit&#8221;). Wcześniej część tej wiedzy znajdowała się w kontrolerze.</li>
<li>Przyjemny side-effect wynikający z używania parametrów &#8220;named&#8221;: Teraz komponując link w widoku zamiast zastanawiać się czy formularz dodawania kosztów dla danej firmy jest pod /costs/add//[id_firmy]/ czy pod /costs/add///[id_firmy] &#8211; wiem, że jest pod /costs/add/entry_id:[id_firmy]</li>
</ol>
<p>Jest oczywiście brak w tym rozwiązaniu &#8211; dość sporo kodu wędruje do widoku (który, jak wspomniałem, trudno testować). Jednak pozostawienie architektury w pierwotnym stanie tej kwestii nie rozwiązuje. Na początku masz problem z </p>
<ul>
<li>dużą ilością kodu w kontrolerze</li>
<li>kodem kontrolera, który nie jest pokryty testami więc nie będzie refaktoryzowany</li>
<li>kodem w widoku, który nie jest pokryty testami</li>
</ul>
<p>Po zastosowanej zmianie zostanie Ci tylko ostatni punkt. Samodzielnie możesz zdecydować, czy to się opłaca.</p>
<!-- PHP 5.x -->]]></content:encoded>
			<wfw:commentRss>http://blog.grzegorzpawlik.com/2010/05/fat-model-skinny-controller-przyklad/feed/</wfw:commentRss>
		<slash:comments>2</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>
	</channel>
</rss>

