Seminarski rad na praksi

Tema: CakePHP, MVC (Model – View – Controller) Juli, 2007

Mentor: Mustafa Hodžić

Student: Jasmina Šero

SADRŽAJ
Seminarski rad na praksi......................................................................................................1 SADRŽAJ............................................................................................................................2 1. Uvod.................................................................................................................................3 2. MVC (Models – Views – Controllers).............................................................................4 2.1. Model........................................................................................................................5 2.1.1. Definiranje i upit sa hasOne: .............................................................................7 2.1.2. Definiranje i upit sa belongsTo..........................................................................8 2.1.3. Definiranje i upit sa hasMany............................................................................9 2.1.4. Definiranje i upit sa hasAndBelongsToMany (HABTM)...............................11 2.2. View .......................................................................................................................20 2.3. Controller................................................................................................................23 2.3.2. Controller callback...........................................................................................27 Možemo kreirati callback u našem aplikacijskom Model-u ili Controller file-u. U ovom primjeru šaljemo email iz Model-a....................................................................................27 3. Helper.............................................................................................................................28 3.1. HTML Helper........................................................................................................28 3.2. TimeHelper............................................................................................................31 3.3. NumberHelper.........................................................................................................32 4. Scaffolding.....................................................................................................................34 5. Data Validation .............................................................................................................35 6. Components...................................................................................................................37 7. Servis “Anketa”.............................................................................................................39 7.1. Tabele servisa..........................................................................................................39 7.2. MVC servisa...........................................................................................................41 7.2.1. Model ..............................................................................................................41 7.2.2. View.................................................................................................................42 7.2.3. Controller.........................................................................................................46 7. 3. Upustvo za servis..................................................................................................52 ZAKLJUČAK....................................................................................................................59 LITERATURA..................................................................................................................60

2

1. Uvod
CakePHP je open source web aplikacijski framework napisan za PHP, distribuiran od MIT license. Razvoj Cake-a počinje u 2005-oj godini od tada je nastalo nekoliko projekata i još uvijek se radi na razvoju novih. CakePHP je dobro dokumentovan framework, inspiriran konceptom predstavljenim u RoR-u (Ruby on Rails), koji može biti primjenjen u PHP4 – PHP5. Cake olakšava komunikaciju user interface-a sa bazom podataka, brz je, koristi MVC ( Model – View – Controller ) arhitekturu, te time forsira objektno i uredno programiranje. Ima podršku za cashe, ajax, RSS Helper, a uskoro bi trebao dobiti još “masu” stvari. Kod testiranja i debagiranja aplikacije bilo koji developer koji primjenjuje CakePHP strukturu će biti sposoban da locira i ispravi grešku bez znanja svih detalja o kodu. Neke osobine CakePHP- a: Kompatibilan je sa PHP4 i PHP5 Fleksibalan View Cashing Application Scaffolding Access Control List View Helpers za AJAX, Javascript, HTML forme,... Radi iz bilo kojeg website poddirektorija Brz, fleksibilan template (PHP sintaksa sa Helper metodama) Ugrađena validacija Website directory nezavisan View caching

Da bi koristili CakePHP moramo imati HTTP sever (wamp, apache), PHP 4 ili 5, database engine (MaSQL, PostgreSQL). Instalacija Cake-a: downloadujemo jednu od verzija CakePHP-a (www.cakephp.org, www.forgephp.org ). Otpakujemo folder i snimimo na web server u webroot direktorij ( wamp, apache,…npr. C:\wamp\www), zatim učitamo stranicu kao localhost u web browser-u. Indexnu stranu podešavamo u /app/config/routes.php file-u, detalje o podešavanju i instalaciji CakePHP-a možemo naći u CakePHP Manual dokumentu, (www.cakephp.org ).

3

a View generira HTTP odgovor. to je kao konceptualna paradigma. Developeri često žele odvojiti podatke (model) od korisničkog interfejsa (view). Svi naši entiteti (Models). koji zatim odabire View za prikaz rezultata tj. Controller prima svaki HTTP zahtijev. koja postoji kao veza između tri objekta: Model. a controller je kod koji skuplja dinamičke podatke i generira sadržaj sa HTML-om. tako da nećemo mijenjati Contoller ako želimo promijeniti izgled nekog input polja. View i Controller. Model je predstavljen stvarnim sadržajem. funkcije ovih entiteta (Controllers). gdje je view ustvari HTML stranica. kao što možemo vidjeti na sljedećoj slici Input -> Processing -> Output Controller -> Model -> View 4 . MVC često vidimo u web aplikacijama. bilo koji input dolazi preko Controllera. MVC (Models – Views – Controllers) MVC je arhitekturalni patern koji se koristi u softverskom inžinjeringu. tako da mijenjaju korisnički interfejs koji ne daje podatke i podaci mogu biti reorganizovani bez mijenjanja interfejsa. uključujući komponentu controller. MVC je software dizajn patern koji pomaže u logičkom odvajanju koda. u kompleksnim kompjuterskim aplikacijama koje predstavljaju dugi niz korisničkih podataka.… Controller i View zavise od Model-a.2. korisnički interfejs (Views) smješteni su u odvojenim fajlovima. koji je obićno smješten u bazu podataka ili XML fajl. MVC rješava ovaj problem odvajanjem pristupa podacima od biznis logike iz prezentacijskih podataka i korisničkog interfejsa. zato što zahtijevaju podatke iz Model-a.

1.- Model enkapsulira aplikacijeske podatke. Model 5 . aplikacijski flow i business logic View ekstraktuje podatke iz Modela i formatira ih za prezentaciju Controller pristupa aplikacijskom flow. views (pogledi) i kontroleri nalaze se u predefinisanom direktoriju sa CakePHP– ovom direktori strukturom. prima ulaze (zahtijeve) i prevodi ih za Model i View Modeli. kao što se može vidjeti u sljedećem primjeru: • app/ o o o o o o o o config/ controllers/ models/ plugins/ tmp/ vendors/ views/ webroot/ • cake/ config/ docs/ libs/ vendors/ o o o • 2.

Po defaultnu imena modela su u jednini i imaju ime isto kao i tabela u bazi. strani ključ u “authors” tabeli za povezivanje sa Post modelom koristi asocijaciju Authors belongs to. ime modela za tabelu “posts” je “Post”. ako imamo tabelu “answers”. ako želimo snimiti informacije o blog postu i autorima. ponaša se kao primarni drajver u aplikaciji. imena tabela trebaju biti “posts” i “authors”.Simple Domain Model ima one – to – one odgovornost između business objekata i tabela u bazi podataka .tj. jer scaffolding pronalazi i koristi asocijacije između modela. .Model je objekt MVC arhitekture. s tim da je ime tabele u množini.hasOne . odvaja prezentacijku logiku od aplikacijske. možemo koristiti scaffolding da predstavimo sebi aplikacijske podatke. Naravno. nema nikakve prezentacijske osobine i nikakvu odgovornost prema HTTP zahtijevima.Rich Domain Model uključuje kompleksne web objekte i mnoge dizajn paterne Koji model ćemo koristiti zavisi od konteksta aplikacije.php <?php class Answer extends AppModel { var $name = 'Answer'. Jedna od najmočniji osobina CakePHP je veza između modela. sadrži aplikacijske podatke i logiku. npr. možemo uvijek podesiti model asocijacije da rade bez CakePHP konvencije imenovanja. trebamo imati i model “Answer”. Može se klasificirati u dvije kategorije: . npr. Postoje četiri tipa veza: . Za korektno korištenje asocijacije najbolje je slijediti CakePHP konvenciju imenovanja. .belongsTo .hasMany . podatke i probleme aplikacije odvaja. a ime modela za tabelu ”authors” je “Author”.Foreign (strani) ključ: [ime modela u jednini]_id. } ?> Domain Model je layer objekt koji apstraktnu logiku.hasAndBelongsToMany Kada je veza između modela definisana.Ime modela: [ime tabele u jednini]. Omogućava pristup bazi podataka i tabelama u bazi. npr.Ime tabele: [ime objekta u množini]. 6 . Cake automatski povezuje modele. CakePHP konvencija imenovanja odnosi se na foreign (strani) ključ. ime modela i ime tabele: . starni ključ u “authors” tabeli bi trabao biti “post_id”. Ukoliko koristimo CakePHP konvenciju imenovanja. Primjer: Answer model se treba snimiti u app/models/answer.

npr. npr. 'foreignKey' => 'user_id' ) ). 'conditions' => ''. Scaffolding očekuje asocijacije u istom poretku kao što su ključevi u tabeli. } ?> Varijablu $hasOne Cake koristi za pravljenje asocijacije između User i Profile modela. tako da imamo asocijaciju hasAndBelongToMany (Post hasAndBelongToMany Tags).CakePHP scaffolding očekuje asocijacije u istom poretku kao i kolona.name ASC” 7 - . editor_id. Definiranje i upit sa hasOne: <?php class User extends AppModel { var $name = 'User'. var $hasOne = array('Profile' => array('className' => 'Profile'. Editor i Publisher. svaki user ima jedinstven profil što omogućava asocijaciju hasOne (User hasOne Profile). Međutim user-i mogu dodavati i komentare pa imamo asocijaciju hasMany (User hasMany Comments). postavimo vrijednost ključa koristeći SQL redoslijed: “Pofile. Primjer: Ako želimo kreirati jednostavni user menađment sistem za blog. Možemo koristiti ovo da kežemo Caku-u da samo poveže Profile koji ima green header. 'dependent' => true.header_color = ’green’ ”. order: redoslije asocijacija modela. druga Editor i posljednja Publisher.1. da definišemo ovakav uslov treba specifirati SQL uslov: “Profile. Svaki ključ u nizu dozvoljava nam dalju konfiguraciju asocijacije: className: ime klase modela koji želimo povezati . Kada želimo da user sistem radi dozvolit ćemo Posts koji će biti povezan sa Tag objektima. publisher_id. Trebamo i tri ključa: author_id. prva Author. ako želimo specifirati “Profile” ime klase modela uslovi: SQL fragmenti stanja (uslova) koji definišu taj odnos. 'order' => ''.1. 2. ako želimo veze modela u specifičnom redoslijedu. tako ako imamo Article koji belongsTo (pripada) modelima: Author.

Definiranje i upit sa belongsTo Međutim User može zahtijevati da vidi Profil. print_r($user). putanja : /app/models/profile. kao što je: [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) ) 2. 'order' => ''. pridruženi model je uništen kada je uništen predhodni forgeinKey: ime stranog ključa koji povezuje odgovarajući model $user = $this->User->read(null. trebali bi vidjeti asocijaciju User modela.1. to ćemo omogućiti pomoću asocijacije belongsTo koju kreiramo u modelu Profil. } ?> 8 .- zavisnost: ukoliko je podešena na true. var $belongsTo = array('User' => array('className' => 'User'. //output: Array ( [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [password] => c4k3roxx ) Kada izvršimo find() ili findAll() poziva korišteni Profile model. primjer. 'conditions' => ''.2.php <?php class Profile extends AppModel { var $name = 'Profile'. 'foreignKey' => 'user_id' ) ). '25').

treba napraviti sistem da User arhivu poveže sa Comment arhivom. 'foreignKey' => 'user_id'.1. '4'). var $hasMany = array('Comment' => array('className' => 'Comment'.3.$belongsTo niz Cake koristi da poveže modele Profil i User. 'exclusive' => false. U User model trebamo dodati: <?php class User extends AppModel { var $name = 'User'.moderated = 1'. 'dependent' => true. 'finderQuery' => '' ) ). 'order' => 'Comment. Definiranje i upit sa hasMany Sada kada su User i Profile model povezani i rade.created DESC'. Kada izvršimo find() ili findAll() poziv Profil modela. trebali bi vidjeti pridruženi User model kao što je: $profile = $this->Profile->read(null. //output: Array ( [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [password] => c4k3roxx ) ) 2. 'limit' => '5'. 'conditions' => 'Comment. 9 . print_r($profile).

modereted = 1” . trebali bi vidjeti povezane Comment modele. '25'). svi povezani objekti će biti obrisani u jednoj SQL izjavi bez da se beforeDelete callback pokrene . print_r($user).finderQuery: specifikacija kompletne SQL izjave za dohvat veze.limit (ograničenje). “Comment. “Comment.maksimalan broj veza modela. kao što je: $user = $this->User->read(null.Cake koristi $hasMany niz da napravi vezu User i Comment modela. npr.className: ime modela koji želimo povezati . ako želimo povezati modele u određeni redoslijed. koji želimo da nam Cake dohvati. postavimo vrijednost ključa da koristi SQL redoslijed. Možemo koristiti uslov da kažemo Cake-u da poveže samo Comment koji je ograničen. već samo 5 . Svaki ključ u nizu dozvoljava dalju konfiguraciju veze: . //output: Array ( [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [password] => c4k3roxx ) [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) 10 . ovo je dobar način za kompleksne asocijacije koje ovise o više tabela Kada izvršimo find() ili findAll() poziv User modela.exclusive: ako je posatvljeno na true. u ovom primjeru nismo htjeli sve komentare.created DESC” .redoslijed: redoslijed povezani modela. npr.foreignKey: ime stranog ključa koji povezuje pridruženi model .uslovi: SQL fragmenti stanja (uslova) koji definišu taj odnos.

Ne definiranje asocijacija iz oba modela je često uobičajna greška kada pokušamo koristiti scaffolding. tako da oba modela mogu vidjeti jedan drugi. asocijacija 11 . Definiranje i upit sa hasAndBelongsToMany (HABTM) HasAndBelongsToMany je najteži za “pravljenje”. really nice to have. Pridružene tabele sadrže individualne redove koji su povezani jedan sa drugim. ) [3] => Array ( [id] => 285 [user_id] => 25 [body] => The hasMany assocation is extremely nice to have. Razlika između hasMany I hasAndBelongsToMany je ta što sa hasMany. ) [1] => Array ( [id] => 256 [user_id] => 25 [body] => The hasMany assocation is really nice to have. ali je takođe i najviše korišten. ) ) ) Dobra ideja je definirati “Comment belongsTo User” asocijaciju. ) [4] => Array ( [id] => 286 [user_id] => 25 [body] => The hasMany assocation is super nice to have. 2.koristan je kad imamo dva modela koja su povezana zajedno sa pridruženim tabelama. ) [2] => Array ( [id] => 269 [user_id] => 25 [body] => The hasMany assocation is really.[Comment] => Array ( [0] => Array ( [id] => 247 [user_id] => 25 [body] => The hasMany assocation is nice to have.4.1.

PRIMARY KEY (`post_id`. `modified` datetime default NULL. asocijacije modela su dijeljene. Za naš primjer to su “post_id” i “tag_id”. sa HABTM. PRIMARY KEY (`id`) ) TYPE=MyISAM.`tag_id`) ) TYPE=MyISAM. `user_id` int(10) default NULL.Categories i Articles: articles_categories HABTM da poveže tabele treba najmanje dva strana ključa modela koji su povezani. -. `title` varchar(50) default NULL. `status` tinyint(1) NOT NULL default '0'.Posts i Tags: posts_tag . `tag` varchar(100) default NULL. PRIMARY KEY (`id`) ) TYPE=MyISAM. `tag_id` int(10) unsigned NOT NULL default '0'.Table structure for table `tags` -CREATE TABLE `tags` ( `id` int(10) unsigned NOT NULL auto_increment.modela nije dijeljena (shared). -. Ako imamo User hasMany Comments. `created` datetime default NULL. samo je user povezan sa komentarima.Table structure for table `posts_tags` -CREATE TABLE `posts_tags` ( `post_id` int(10) unsigned NOT NULL default '0'. jednostavni modeli i pridružena im imena tabela: . `body` text. HABTM pridružene tabele.---------------------------------------------------------.Table structure for table `posts` -- CREATE TABLE `posts` ( `id` int(10) unsigned NOT NULL auto_increment. Primjer Posts HABTM Tags: --. 12 .Monkeys i IceCubes: ice_cubes_monkeys .---------------------------------------------------------.

print_r($post). 'conditions' => ''. trebali bi vidjeti pridruženi Tag model kao što je: $post = $this->Post->read(null. [created] => 2006-04-15 09:33:24 [modified] => 2006-04-15 09:33:24 [status] => 1 ) [Tag] => Array ( [0] => Array ( [id] => 247 [tag] => CakePHP ) 13 . ) ). 'foreignKey' => 'post_id'. } ?> Cake koristi $hasAndBelongsToMany niz da napravi asocijaciju između Post i Tag modela. and powerful. '2'). 'deleteQuery' => ''. 'order' => ''. 'limit' => ''.php hasAndBelongsToMany <?php class Post extends AppModel { var $name = 'Post'. 'joinTable' => 'posts_tags'. //output: Array ( [Post] => Array ( [id] => 2 [user_id] => 25 [title] => Cake Model Associations [body] => Time saving.Sa ovim tabelama definišemo asocijaciju u Post modelu: /app/models/post. var $hasAndBelongsToMany = array('Tag' => array('className' => 'Tag'. 'uniq' => true. easy. Kada izvršimo find() ili findAll() poziv Post modela. 'finderQuery' => ''. 'associationForeignKey'=> 'tag_id'.

Treba da sadrži metode koje su dijeljene između dva ili više modela. } } function unhide ($id=null) { if ($id) { $this->id = $id.1. Primjer specifične metode u modelu je par metoda za hiding/unhiding post u blogu.1. $this->saveField('hidden'. $this->saveField('hidden'.[1] => Array ( [id] => 256 [tag] => Powerful Software ) ) ) 2.php. '1'). Time ona proširuje klasu Model koja je standardna Cake biblioteka definisana u cake/libs/model. treba je smjestiti u /app/app_model. ali ako želimo kreirati vlastitu. } 14 .php. Model funkcije Modeli su klase proširene AppModel klasom. function hide ($id=null) { if ($id) { $this->id = $id. '0'). AppModel klasa je orginalno definisana u cake/ direktoriju. kao što vidimo u sljedećem primjeru: <?php class Post extends AppModel { var $name = 'Post'.

array $fields.} } ?> U sljedećem primjeru vidimo nekoliko standardni načina za dobivanje naših podataka koristeći model: findAll ($conditions thermal_detonators > 3". $order. $limit. $fields. Ove magične funkcije mogu biti korištene kao shortcut za pretragu tabela za redom koji je dat određenim poljem i određenom vrijednošću. 15 . int $limit. findAll( ) operacije pokušavaju vratiti odgovarajući model nađen sa findAll( ). $recursive). primjer: $this->Post->findByTitle('My First Blog Post'). $recursive. int $recursive. find ($conditions $fields. string $conditions. Povratni rezultat je niz formatiran koji je nađen sa find() ili findAll( ). Vraća specifično polje iz prvog rekorda koje odgovara $conditions. $this->Specimen->findAllByKingdom('Animalia'). $recursive). $this->Property->findAllByState('AZ'). findAll( ) na našem Property modelu će vratiti te odgovarajuće modele. $order. $page. Ako naš property ima više vlasnika koji su uključeni u više ugovora. $fields. string $order. int $page. Treba samo izabrati ime polja koji želimo tražiti. string array string int $conditions. $this->Author->findByLastName('Rogers'). //primjer:$conditions="race='wookie' AND Kada je $recursive opcija postavljena na vrijednost veću od 1. $order.

Npr. string $value. puni poziv može izgledati kao u sljedećem primjeru: $this->set( 16 . $keyPath. $value). } } findCount ($conditions). $order. Primjer: class ImagesController extends AppController { function view($id) { // Say we want to show the image. Vraća broj rekorda koji odgovaraju zadanim uslovima. specifiran sa $field i $value i filtriran sa SQL uslovima. $conditions. ako želimo generirati listu uloga baziranu na Role modelu. 'id'. generateList ($conditions. $this->Image->findNeighbours(null.. Vraća niz sa susjednim modelom (sa samo navedenim poljima).findNeighbours ($conditions $field. $limit. array $field. $order. $id).. $this->set('image'. $valuePath. Ovo radi samo za numerička i date polja. $limit. string $conditions. string $conditions.. Ovo može biti korisno u situaciji kada želimo 'Previous' i 'Next' linkove koji vode korisnika kroz uređenu sekvencu u našem model-u ulaza. $valuePath).. Ova funkcija je shortcut za dobivanje liste ključnih vrijednosti parova – pogotovo praktična za kreiranje html select tagova iz liste našeg modela. string string int string string $conditions. $keyPath. // But we also want the previous and next images. $this->Image->find("id = $id"). $this->set('neighbours'.

'Roles', $this->Role->generateList(null, 'role_name ASC', null, '{n}.Role.id', '{n}.Role.role_name') ); //This would return something like: array( '1' => 'Account Manager', '2' => 'Account Viewer', '3' => 'System Manager', '4' => 'Site Visitor' ); read ( $fields, $id); string $fields; string $id;

Ovu funkciju možemo koristiti da dobijemo polja i vrijednosti polja iz trenutačno učitanog rekorda ili rekorda specifiranog sa $id.
query ($query); string $query; execute ($query); string $query;

SQL poziv možemo napraviti koristeći model query( ) i execute ( )metode. Razlika između query( ) i execute ( ) je ta što, query( ) koristimo da napravimo korisnički SQL upit, a execute( ) koristimo da napravimo korisničke SQL komande. Primjer:
<?php class Post extends AppModel { var $name = 'Post';

} ?>

function posterFirstName() { $ret = $this->query("SELECT first_name FROM posters_table WHERE poster_id = 1"); $firstName = $ret[0]['first_name']; return $firstName; }

Većina model pretraživača uključuju prosljeđivanje uslova u jednom ili u drugom pravcu. Najjednostavniji pristup ovome je pomoću WHERE klauzule iz SQL-a, ali ako nam je potrebno više kontrole možemo koristiti nizove. Korištenje nizova je jednosatvno i lako za čitanje i jednostavno je za pravljenje upita. Sintaksa odvaja elemente upita u diskretne, 17

manipulativne dijelove. Ovo dozvoljava Cake-u da generira najviše moguće efikasne upite, osigurava odgovarajuću SQL sintaksu. Upit baziran na nizu izgleda ovako:
$conditions = array("Post.title" => "This is a post"); //Example usage with a model: $this->Post->find($conditions);

Traži bilo koji post gdje naslov odgovara stringu “This is a post”, mogli smo koristiti samo “title” kao ime polja. Kada pravimo upite, dobra je praksa uvijek specificirati ime modela, kao poboljšanje jasnoće koda i kao pomoć pri prevenciji kolizija u budućnosti ako odlučimo mijenjati našu šemu. I sa drugim komparacijama je isto, npr. ako želimo naći sve postove gdje naslov nije “This is a post”.
array("Post.title" => "<> This is a post")

Sve što je dodano nalazi se između ‘< >’ tagova prije izraza. Cake može obraditi bilo koju validnu SQL komparaciju operatora uključujući odgovarajuće izraze koristeći LIKE, BETWEEN. Npr. Ako želimo naći post gdje je naslov dat kao set vrijednosti:
array("Post.title" => array("First post", "Second post", "Third post"))

Dodavanje dodatnih filtera uslovima je jednostavno kao dodavanje dodatnih parova vrijednosti /ključeva nizu.
array ( "Post.title" => array("First post", "Second post", "Third post"), "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")) )

Cake prihvata sve validne SQL bool-ove operacije, uključujući AND, OR, NOT, XOR,...i možemo ih pisati malim ili velikim slovima. Po defaultu, Cake pristupa višestrukim uslovima sa bool-ovim AND, što znači, odlomak koji želimo odgovara samo postu koji je kreiran prije dvije sedmice i naslov odgovara jednom u datom setu. Mi možemo jednostavno naći post koji odgovara bilo kojem uslovu:
array ("or" => array ( "Post.title" => array("First post", "Second post", "Third post"), "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")) ) )

18

Snimanje podataka Da bi snimili podatke u naš model, trebamo dostaviti podatke koje želimo snimiti, podaci koji se šalju save( ) metodi trebaju biti u sljedećoj formi:
Array ( [ModelName] => Array ( [fieldname1] => 'value' [fieldname2] => 'value' ) )

U redoslijedu slanja podataka controller-u u ovom načinu, lakše je koristiti HTML helper za ovo, jer kreira forme elemenata koje su imenovane na način koji Cake očekuje. Samo trebamo biti sigurni da forme elemenata imaju imena kao što je data[Modelname] [fieldname], koristiti $html->input('Model/fieldname') je najjednostavnije. Podaci poslani iz forme se automatski formatiraju i budu smješteni u $this->data u contoller-u, tako da je snimanje podataka iz web formi brzo. Edit funkcija za svojstvo controller-a može izgledati ovako:
function edit($id) { // The property model is automatically loaded for us at $this->Property. // Check to see if we have form data... if (empty($this->data)) { $this->Property->id = $id; $this->data = $this->Property->read();//populate the form fields with the current row } else { // Here's where we try to save our data. Automagic validation checking if ($this->Property->save($this->data['Property'])) { //Flash a message and redirect. $this->flash('Your information has been saved.', '/properties/view/'.$this->data['Property']['id'], 2); } //if some fields are invalid or save fails the form will render } }

Delete funkcija: del ( $id, 19

Brisanje modela specificiramo sa $id ili sa aktualnim id-om modela. string $id.') ?> </p> <p> Body: <?php echo $html->textarea('Post/body'. npr.') ?> </p> <p> <?php echo $html->submit('Save') ?> </p> </form> 20 .2. array('size' => '40'))?> <?php echo $html->tagErrorMsg('Post/title'. nakon uspješnog brisanja. 'Body is required. koji se obično imenuje poslije akcije. tj. 'Title is required. view za PostController::add() nalazi se u /app/views/posts/add. array('rows'=>'10')) ?> <?php echo $html->tagErrorMsg('Post/body'. Ako je model povezan sa drugim modelima. ova metoda će takođe obrisati povezani model. 2. vraća true pri uspjehu. Ako je $cascade posatvljen na true.thtml. <h1>Add Post</h1> <form method="post" action="<?php echo $html->url('/posts/add')?>"> <p> Title: <?php echo $html->input('Post/title'. boolean $cascade. View View je page template.$cascade).

0 Transitional//EN" "http://www. View takođe može biti bilo koji set podataka.2. Layouts Layouts sadrži prezentacijski kod i bilo što što želimo vidjeti u view-u treba biti smješteno u layout-u.dtd"> <html xmlns="http://www.If you'd like some sort of menu to show up on all of your views. View : . Primjer kako može izgledati defaultni layout <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. Popularni template Smarty je primjer template-a koji koristi custem syntax metodu. include it here --> <div id="header"> <div id="menu">. mediu…Mnoge funkcije u view-u su dostupne preko helpera. npr.. eksktaktuje podatke iz Modela i priprema ih kao HTML za web stranicu ili kao XML za web server ili kao tekst za mail. dok u Transform View-u počinje se sa podacima i gradi se output prema tome. Cake views su prilično jednostavni PHP file-ovi. Cake-ov defaultni layout može biti zamijenjen novim defaultnim layout-om kao /app/views/layouts/defaults. HTML Helper je po defaultu dostupan u svakom view-u i to je najkorišteniji helper u view-u. ali nije dobro da View poziva metode Model-a kao što je update.org/TR/xhtml1/DTD/xhtml1-transitional.Views omogućava sve aspekte prezentacije. Template View je primarni patern koji koristi template file (obično HTML) koji uključuje specijalne markere koji su zamijenjeni sa podacima iz modela kada je Template View izvršen. Kada kreiramo layout treba “reči” Cake gdje se nalazi controller. uključuje scripte. Koristan je kod kreiranja formi.org/1999/xhtml"> <head> <title><?php echo $title_for_layout?></title> <link rel="shortcut icon" href="favicon. to bi trebalo uraditi preko Controller-a. Transform View ekstraktuje podatke iz modela i transformiše ih u traženi output format.elements 2. U Template počinje se sa skeletom outputa i insertuju domain podaci u to.ico" type="image/x-icon"> </head> <body> <!-. Cake dolazi sa velikim brojem helper-a. XML. View ima pristup Model-u.. Postoje dva dizajn paterna koja se koriste u view-u: Template View i Transform View.Svi pogledi (views) unutar Cake-a se nalaze u odvojenim fajlovima (*. najviše view file-ova sadrži HTML. Layout file-ovi nalaze se u /app/views/layouts.1. Jednom kada defaultni layout bude kreiran.</div> 21 . Razlika između Template View-a i Transform View-a je u pristupu data flow-u. image. controller view kod biva smješten unutar defaultnog layout-a.thtml) koji su slobodni od kompleksnog PHP koda.w3. a može se kreirati i vlastiti. View ne treba sadržavati puno logike.thtml.w3. linkove. zato nema puno korištenih javnih funkcija u view-u..layouts ..

?> U Element file-u sve propuštene varijable su varijable imenovane kao i niz . 22 . navigacijka kontrola. 2. Cake pomaže da ponavljamo dio website-a koji nam je potreban. Help boksovi.</div> </body> </html> Možemo kreirati koliko hoćemo layout-a u Cake-u samo ih treba smjestiti u /app/views/layouts direktorij. Elementi se nalaze u /app/views/elements/ folderu I imaju .")). array("helptext" => "Oh..ekstra meniji su implementirani u Cake-u kao elementi.2.thtml file ekstenziju. da bi dozvolili pristup podacima šaljemo ih kao imenovani parameter u niz. this text is very helpful. ponekad u različitim mjestima u layout-u. elementi se mogu koristiti da View učine više čitljivim.2. Elements Mnoge aplikacije imaju male blokove prezentacijskog koda koji se treba ponavljati na više stranica.Here's where I want my views to be displayed --> <?php echo $content_for_layout ?> <!-.. Primjer: Poziv elemenata bez parametra Poziv elementa kao niza podataka: <?php echo $this->renderElement('helpbox'. Elementi po defaultu nemaju nikakav pristup podacima. ?> <?php echo $this->renderElement('helpbox').Add a footer to each displayed page --> <div id="footer">.</div> <!-. Element je bazično mini view koji koji može biti uključen u drugi View.

Imamo dvije vrste Controller-a: .php. ime Controllera u Cake-u je uvijek u množini. UsersController. Controller varijable: 23 . Controller Controller procesira i odgovara na događaje.Application Controller Front Controller često pomaže u centralizaciji aplikacijkog flow-a u jednostavnim tačkama. Controller se koristi za logičko upravljanje modelom.php. Command patern enkapsulira akciju u objekte.2. line_items_controller.-'. pa ako imamo controller klasu koja se zove PostsController.3. 2. file controller će biti posts_controller. Centralizacija može pomoći da razumijemo kako kompleksan sistem operira i navodi jednostavna mjesta gdje možemo ubaciti globalni kod. Ime controller klase zavrašava sa „Controller“ npr. tipična korisnička akcija i poziva promjene na modelu. klase AppController-a definisane su u app/app_controller.3. Manipulacija sa nekoliko specijalnih varijabli u controller-u dozvoljava da koristimo neke prednosti extra Cake funkcionalnosti. Ime controller file-a piše se malim slovima i završava sa '_controller'. PeopleController. Da bi zaštitili vidljivost.1. Primarna odgovornost je da odluči šta aplikacija treba odgovoriti na traženi zahtijev. U sadržaju web aplikacije koristan je cilj koda koji odvaja koncentrirani Command da brine o radu pojedinačni HTTP zahtijeva. tako da možemo parametrizirati upit.php i treba da sadrže metode koje su dijeljenene imeđu dva ili više Contoller-a. za privatnu vidljivost. Front Controller je odličan za centralizaciju. really_nifty_things_controller. primjeri: people_controller.php.php.Front Controller .ime treba početi sa '. Application Controller je centar onog čime MVC Controller operira. ime akcije controller-a treba početi sa '-'. Tipičan način impelmentacije Controller-a je koristeći Command paterne.

- - - $uses – ako controller koristi više od jednog modela. var $components = array('acl'). Treba uključiti HtmlHelper u $helpers ako ga želimo koristiti. $helper – ova varijabla se koristi za učitavanje helpera u view. $autoRender .'Ajax'. var $helpers = array('Html'.. function checkAccess() { //Logic to check user identity and access would go here. $this->data Primjer: slanje podataka POST metodom iz HTML Helper-a u controller // A HTML Helper is used to create a form element $html->input('User/first_name').postavljajući ovo na false će zaustaviti akciju od automatskog izvođenja $components – kao $helpers i $uses.Controller parametri Controller parametri su dostupni preko $this->params u Cake controller-u. ova varijabla koristi se za učitavanje komponenata koje su nam potrebne npr. ali ako želimo pristupiti $this->Smurf.3. } function index() { //When this action is called. FragglesController će automatski učitati $this->Fraggle. $beforeFilter – koristimo ovu varijablu ako želimo da se kod pokreće svaki put kada se akcija pozove . ali može se ova varijabla koristiti i za druge helpere npr. Ova funkcionalnost je veoma dobra za access control. } } 2.možemo vidjeti korisničke permisije prije nego se izvrši bilo koja akcija.. 24 . Primjer: class ProductsController extends AppController { var $beforeFilter = array('checkAccess').'Smurf'). dostupan je po defaultu ali ako definišemo $helper bez HtmlHelpera. treba pokušati dodati sljedeću liniju koda u controller: var $uses = array('Fraggle'. dobit ćemo poruku o greški u view-u. Najčešće se koristi da se dobiju informacije (podaci) koje su date controller-u preko POST ili GET operatora.. ova varijabla se koristi da dajemo podatke u controller i omogućava pristup informacijama o tačnom upitu.'Javascript'). Treba primjetiti da smo uključili Fraggle model u $uses niz. checkAccess() is called first. HTML helper se automatski učitava.1. iako je automatski prije bio dostupan.

edit i delete vijesti koje su nalaze u bazi. nizovi.'blue') . ispisuje poruku da je post izbrisan.// When rendered in the HTML would look something like: <input name="data[User][first_name]" value="" type="text" /> // And when submitted to the controller via POST. '/posts'). Save() metoda provjerava greške i neće snimiti podatke ako postoji greška. tj..$id.vraća broj grešaka koje su generirane pri neuspješnom snimanju podataka. Omogućava dodavanje novih vijesti. Funkcija index nam prikazuje početnu stranicu tj. edit ( ) i delete ( ).$value).'.. controller napravi varijablu $color dostupnom u view-u. . stranicu koju smo postavili da bude indexna..validateErrors( ) . pokreće se automatski po zahtijevu.. .Jednom kada koristimo set(). . početna. mixed $value. string $var.'/posts').set( ) funkcija je glavni put da uzmemo podatke iz controller-a da bi ih vidjeli. 25 . .' has been deleted. // shows up in $this->data['User']['first_name'] Array ( [data] => Array ( [User] => Array ( [username] => mrrogers [password] => myn3ighb0r [first_name] => Mister [last_name] => Rogers ) ) ) Akcija je jedinstvena funkcionalnost kontrolera. parametri funkcije mogu biti jednostavne vrijednosti.$this->flash( ) funkcija zove se upravljačka funkcija koja prikazuje korisniku poruku npr. set($var. add ( ). cijele nizove. varijablama može se pristupiti u view-u. koja prikazuje naslov postojeće vijesti. $this->flash ('Your post has been saved. U sljedećem primjeru imamo Post controller sa četiri funkcije: funkcija index ( ). ako napišemo set('color'. Funkciju set( ) možemo koristiti da uzmemo bilo šta: jednosatvne vrijednosti.render ( ) je funkcija koja se često ne koristi. jer render se automatski poziva na kraju svake kontrolne akcije.'. Ispisuje poruku korisniku da je njegov post snimljen ili $this->flash ('The post with id: '.

'.<?php class PostsController extends AppController { var $name = 'Posts'. } } } } ?> function view($id = null) $this->Post->id = $id. } function edit($id = null) { if (empty($this->data)) { $this->Post->id = $id.'. } } function delete($id) $this->Post->del($id).' has been deleted. $this->Post->read()). $this->set('post'.$id. $this->data = $this->Post->read().'/posts').'/posts').'. '/posts'). } { 26 . } else { if ($this->Post->save($this->data['Post'])) { $this->flash('Your post has been updated. function index() { } { } { function add() if (!empty($this->data)) { if ($this->Post->save($this->data)) { $this->flash('Your post has been saved. $this->flash('The post with id: '. $this->set('posts'. $this->Post->findAll()).

beforeFilter ( ).requestAction ( $url. string $url.2. Uzimanje podataka iz controller-a je jednostavno. U ovom primjeru šaljemo email iz Model-a <?php // File: /app/models/bookmark. poziva se prije svake controller akcije. // Ovdje je jednostavni controller class UsersController extends AppController { function getUserList() { $this->User->findAll(). .php class Bookmark extends AppModel { 27 . korisna za provjeru aktivni sesija i provjeru uloga .2. Controller callback U Cake-u callback je način izvršavanja nekog koda prije ili poslije Model ili Contoller metoda. array $options. Ova funkcija poziva controller akciju iz bilo koje lokacije i vraća tj. poziva se nakon svake controller akcije . prikazuje view. Treba deklarisati funkcije u controller-u koristeći parametre i povratne vrijednosti. poziva se nakon controller logike i prije nego što je view prikazan Sljedeće funkcije su dio Cake objekt klase.beforeRender ( ).3. $url je Cake URL (/controllername/actionname/params).afterFilter ( ). AutoRender automatski postavlja true za controller akciju. koristi se samo upitna akcija u view-u u iz kojeg trebamo podatke. $options). Cake controller omogućava odgovarajući broj callback-ova koji možemo iskoristiti da bi ubacili logiku prije ili poslije značajnih funkcija controller-a. ali one mogu biti dostupne i u controller-u . Možemo koristiti requestAction da dobijemo podatke iz druge controller akcije ili da dobijemo potpuni render view iz controller-a. } } Možemo kreirati callback u našem aplikacijskom Model-u ili Controller file-u. ako $extra niz uključuje 'return' .

Ako želimo koristiti Cake za ubacivanje dobro formirani i često korištenih elemenata u HTML kodu. 'Bookmark saved to database'). 3.php i snimimo je u /cake/config/ folder.Time .var $name = 'Bookmark'. ako je prvi argument niz.AJAX .1. tabele.ini..com'.]]) U svakoj funkciji treba provjeriti je li prvi argument niz.HTML . Ako želimo neke promjene napravimo kopiju /cake/config/tags. mixed $secondArg = null[.Number . Postoje funkcije u ovom helperu koje insertuju mediu. Helper Cake ima nekoliko već kreiranih helpera koje možemo iskoristiti. return true. HTML Helper je veoma dobar u tome.… Generiranje charset MET-a taga: charset ( $charset. treba provjeriti specifični ključ sa funkcijom array_key_exists ( ). HTML Helper funkcije takođe uključuju $htmlAttributes parametre. } } ?> 3.ini. Omogućiti brzo i jednostavno kreiranje web formi Mnoge funkcije u HTML Helper-u koriste HTML tagove definisane u file-u tags. 28 . HTML Helper HTML Helper je jedan od načina Cake-a za brži i manje monoton razvoj. HTML Helper ima dva cilja: 1.Javacript .php. . a to su: . to provjeravamo sa funkcijom is_array ( ).Cache Svaka metoda u helperu treba da prihvati argumente u sljedeći način: helperMethod(mixed $params[. $return ).. Omogućiti ubacivanje često ponovljene sekcije HTML koda 2. function afterSave() { // mail me when a new bookmark is added mail('example@example. liste.Text .

?> CakePHP nam vraća: <link rel="stylesheet" type="text/css" href="/cake1point2/css/donutczar. ?> CakePHP nam vraća: 29 .cakephp.'email')).css" /> koji učitava stil za našu stranicu Ako napišemo: <?php echo $html->link('CakePHP Home'. //$rel parameter dozvoljava kreiranje HTML Helperi Ako napišemo: <?php echo $html->css('donutczar'). $htmlAttributes.cakephp. $rel=’stylesheet’.org" >CakePHP Home</a> koji vidimo kao CakePHP Home Ako napišemo: <?php echo $html->tableHeaders(array('First Name'.'Last Name'. $rel=’stylesheet’. rel=vrijednost za tag $htmlAttributes.'Ceiteach'. ?> Cake nam vraća: <tr><th>First Name</th> <th>Last Name</th> <th>Email</th></tr> što prikazuje kao First Name Ako napišemo: Last Name Email <?php echo $html->tableCells(array('Dunnottar'.'http://www. $return = ‘false’). boolean $return. $return = ‘false’.org'). string string array boolean $path. Kreiranje linka u CSS stylesheet-u : css( $path.string $charset. ?> Cake nam vraća: <a href="http://www.).'Email').

} else { $title = $params.png" alt="CakePHP icon" /> što prikazuje Primjer definicije HtmlHelper::link ( ) <?php function link($params. } if (isset($confirm)) { $options['confirm'] = $confirm. EXTR_OVERWRITE).icon. } if (!isset($href)) { $href = $title.png'. unset($confirm). } if (!isset($title)) { $title = null.array('alt'=>'CakePHP icon')). ?> CakePHP nam vraća: <img src="/cake1point2/img/cake. $confirm = null) { if (is_array($params)) { extract($params. $options = null. } if (strpos($href. } if (!isset($options)) { $options = null. $href = null. '://') === false) { $href = $this->url($href).<tr><td>Dunnottar</td> <td>Ceiteach</td> <td>email</td></tr> što prikazuje : Dunnottar Ako napišemo: Ceiteach email <?php echo $html->image('cake. 30 .icon.

string array boolean $filedName.. 'tagValue' => $title ).} $values = array( 'href' => $href. 3. string $buttonCaption. $return = false. $return = false).. ?> 31 . $htmlAttributes.'). $values).nice Ako napišemo: <?php echo $time->nice('07-11-2007 18:13:15'). boolean $return = false. TimeHelper . submit ( $buttonCaption.'. array $htmlAttributes.. return $this->assign('link'. $return = false).2. $htmlAttributes. 'options' => $this->_parseOptions($options).trim Ako napišemo: <?php echo $time->trim('Source character string. } ?> Form tagovi koje HTML Helper može generirati: . . ?> CakePHP vraća: Source chara.password .'.12. $htmlAttributes..submit password ($fieldName.

2.'. Apr 29th 2013.toPercentage Ako napišemo: <?php echo $number->toPercentage('98.dayAsSql Ako napišemo: <?php echo $time->daysAsSql('07-11-2007 18:13:15'. ?> CakePHP vraća: $98. ?> 32 .43 .currency Ako napišemo: <?php echo $number->currency ('98765.'.'07-01-2007 00:00:00'.'created').765. ?> CakePHP vraća: (created >= '2013-04-29 00:00:00') AND (created <= '2012-06-29 23:59:59') .4321'.4321'.'').CakePHP vraća: Mon.3).7654321'.2). ?> CakePHP vraća: 123. 18:13 . ?> CakePHP vraća: 98.precision Ako napišemo: <?php echo $number->precision('123456789'.format Ako napišemo: <?php echo $number->format('98765.isToday Ako napišemo: <?php echo $time->isToday('07-11-2007 18:13:15').457 . NumberHelper .77% .3. ?> CakePHP vraća true ako je dati datum današnji 3.'USD').

.4. string $string. boolean $return = false. prvo trebamo kreirati novu klasu u app/views/helpers..toReadableSize Ako napišemo: <?php echo $number->toReadableSize('123456789'). Kreiranje vlastitih helpera Konvencija imenovanja je slična kao u modelu: • • • LinkHelper = class name link = key in helpers array link. $url) { 33 . } } Postoji nekoliko funkcija uključenih u Cake helper klase koje možemo iskoristiti: output ( $string. file php klase trebao bi izgledati ovako: Putanja: /app/views/helpers/link. ?> CakePHP vraća: 117.php class LinkHelper extends Helper { function makeEdit($title. Ako koristimo output ( ) za formatiranje naših linkova title i URL i vraćanje ih u view.php class LinkHelper extends Helper { function makeEdit($title.php = name of php file in /app/views/helpers Ako želimo kreirati helper koji će koristiti output CSS style linka koji trebamo u aplikaciji. npr. $return = false). da se naš helper zove LinkHelper.765.php trebamo dodati: Putanja: /app/views/helpers/link.CakePHP vraća: 98.43 .74 MB 3. $url) { // Logic to create specially formatted link goes here. u link.

$url) { // Use the HTML helper to output // formatted data: $link = $this->Html->link($title. zatim pišemo funkcije kao u controller-u npr. 'Link'). Scaffolding Scaffolding je dobar način uzimanja početnog dijela web aplikacije. Web developeri ne vole kreirati forme koje nikad neće “stavrno” koristiti. array('class' => 'edit')). delete i edit dugmićima. class ThingsController { var $helpers = array('Html'. Putanja: /app/views/helpers/link. } } Možemo koristiti neke već postojeće funkcionalnosti drugog helpera. $url. } } return $this->output("<div class=\"editOuter\">$link</div>"). } 4. Kada kreiramo vlastiti helper snimimo ga u /app/views/helpers/ i bit ćemo u mogućnosti da ga uključimo u naš contoller-u preko specijalne varijable $helpers. npr.php ( korištenje drugi helpera) class LinkHelper extends Helper { var $helpers = array('Html'). što je normalno u početnom dijelu dizajna procesa. početna šema baze i tema se mijenjaju. scaffolding je uključen u Cake. Da koristimo prednosti drugi helpera trebamo specificirati helper koji želimo koristiti. function makeEdit($title.// Use the helper's output function to hand formatted // data back to the view: return $this->output("<div class=\"editOuter\"><a href=\"$url\" class=\"edit\">$title</a></div>"). Scaffolding analizira tabele u bazi i kreira standradnu listu sa add. Strandardnu formu za edit i standardni 34 . $helper nizom. Da bi smanjili pritisak na developere.

Da postavimo koje polje u stranoj tabeli će se prikazivati. korisničko ime može sadržavati samo slova.thtml /app/views/posts/scaffold/show. primjer: 35 .scaffold. Ako imamo tabelu category i kolonu parent_id. npr.… Prvi korak u data validation je kreiranje validacijskih pravila u Model-u. Ako želimo nešto drugačiji scaffolding view. možemo kreirati vlastiti. Za dodavanje scaffoldinga u aplikaciju. } ?> var $displayField = 'title'. } ?> Scaffold očekuje da bilo koje ime polja koje završava sa “_id” je strani ključ za tabelu koja ima ime koje predhodi “ _”.thtml /app/views/posts/scaffold/edit. Kada imamo strani ključ u tabeli (tabela title sa category_id) i imamo asocijaciju modela. lozinka može biti samo osam karaktera. npr. Primjer: <?php class Title extends AppModel { var $name = 'Title'.scaffold.thtml /app/views/posts/scaffold/new. Primjer: Custom scaffolding views for a PostsController should be placed like so: /app/views/posts/scaffold/index. u controller treba dodati varijablu $scaffold. <?php class CategoriesController extends AppController { var $scaffold. Da bi to uradili koristimo Model:: validate array u Model definiciji.thtml Osobina koja može koristiti u Cake-u je kod generator: Bake. postavimo varijablu $displayField u strani model. označeni box će se automatski popuniti sa redom iz strane tabele (category) u show/edit/new view. Data Validation Kreiranje korisnički validacijski pravila pomaže da podaci budu sigurni u Model-u uz pravila aplikacije.scaffold.view za provjeru jednostavni item-a u bazi. koju zatim možemo premjestiti i modifikaovati prema zahtijevima aplikacije. parent_id je strani ključ za tabelu parent. Bake dozvoljava da generiramo kodiranu verziju scaffold koda.scaffold. 5.

Putanja: /app/models/user. } ?> Validacije se definišu koristeći Perl kompatibilne regularne ekspresije. the stuff is valid } else { //Danger.php.php <?php class User extends AppModel { var $name = 'User'.'). function add () { if (empty($this->data)) { $this->render(). } 36 . Ali su obično podaci u kodu controller-a. 'born' => VALID_NUMBER ). to su: VALID_NOT_EMPTY VALID_NUMBER VALID_EMAIL VALID_YEAR Ako postoji bilo koja validacija u modelu definicije ($validate array) bit će analizirana i provjerena tokom snimanja (Model:: save() method). var $validate = array( 'login' => '/[a-z0-9\_\-]{3. $this->set('errorMessage'. } else { if($this->Post->save($this->data)) { //ok cool.php <?php class BlogController extends AppController { var $uses = array('Post'). 'Please correct errors below. neke od njih su predefinisane u /lib/validators. primjer: /app/controllers/blog_controller. Will Robinson. 'password' => VALID_NOT_EMPTY.}$/i'. 'email' => VALID_EMAIL. Za direktnu validaciju podataka koristimo Model::validates() (vraća false ako su podaci pogrešni) i Model::invalidFields() (vraća error message kao niz). $this->render(). Validation errors.

u file-u trebamo definirati klasu koja ima ime isto kao i komponenta.) to je biznis logika.} } ?> } View za blog može izgledati ovako: /app/views/blog/add. 'Title is required. 'Body is required. dok helperi enkapsuliraju prezentacijsku logiku. class FooComponent extends Object { var $someVar = null.thtml <h2>Add post to blog</h2> <form action="<?php echo $html->url('/blog/add')?>" method="post"> <div class="blog_add"> <p>Title: <?php echo $html->input('Post/title'. del. 37 . add. to je prezentacijska logika. var $controller = true. 6. Komponente su za controller. tako da je ova autentifikacija komponenta. Možemo kreirati i vlastite komponente. logout. tako što dodamo file u /app/controllers/components direktorij. tako da može biti prikazana sa tagErrorMsg() u view-u.$model…] se koristi za provjeru bilo koje korisničke validacije dodane u model. Ako želimo dodati još neke unose na glavni meni kada je korisnik logiran. array('size'=>'40'))?> <?php echo $html->tagErrorMsg('Post/title'. nego proširene Cake biblioteke.. Primjer: ako želimo kreirati komponentu foo.')?> </p> <p>Body <?php echo $html->textarea('Post/body') ?> <?php echo $html->tagErrorMsg('Post/body'. Developeri više vole koristiti specijalne funkcije koje prave u komponentama.php. Controller::validationErrors() vraća bilo koju poruku o grešci u modelu. Primjer: autentifikacija korisnika. Components Komponente se koriste kao pomagalo controller-a u specifičnim situacijama. isto što i helper za view. Glavna razlika u tome je što komponente enkapsuliraju biznis logiku. ako želimo login. permisije za korisnike (akcije: edit.')?> </p> <p><?=$html->submit('Save')?></p> </div> </form> Controller::validates($model[..

Komponente imaju dozvolu controller-a da se učitaju u startup() metodi. 38 .function startup(&$controller) { // This method takes a reference to the controller which is loading it. Možemo koristiti i druge komponente u vlastitoj komponenti. trebamo kreitati instancu: $foo =& new Foo(). } function doFoo() { $this->someVar = 'foo'. jednostavno u našoj komponenti deklariramo koju komponentu želimo kotistiti. Ovo nam dozvoljava da postavimo osobine komponente u beforeFilter() metodu koja komponenta će se izvršiti u startup() metodi. // Perform controller initialization here. Sada u controller-u možemo koristiit: $this->Foo->doFoo(). ova metoda se poziva poslije Controller::beforeFilter(). Da bi koristili naš model u komponenti. Primjer: ako želimo koristiti session komponentu: var $components = array('Session'). } } Da bi koristili komponentu trebamo dodati sljedeći kod u definiciju controller-a var $components = array('Foo').

7. Servis “Anketa” 7.1. Tabele servisa Servis se sastoji od pet tabela: 39 .

ime ankete (name). 40 . Tabela „polls“ je u odnosu 1:1 sa tabelom „questions“ tj. polls .u tabelu „polls“ smještamo id (primarni ključ tabele). jedna anketa može imati samo jedno pitanje. jer jedna anketa može imati više rezultata. broj korisnika koliko ih je glasalo i broj odgovora (noq) tj. broj odgovora koliko će imati anketa. a 1:n u odnosu na tabelu „results“. ukupno odgovora (ukupno_odg) tj.1.

jedno pitanje može imati više odgovora.tj.2. polje anketa_id povezuje sa tabelom „polls“ u odnosu 1:1. kao što možemo vidjeti u sljedećem primjeru. polje anketa_id povezuje tabelu „questions“ sa tabelom „polls“ u odnosu 1:1.2.2. svaki rezultat (odgovor) povezan je sa anketom. U koloni rezultat smješteni su rezultati odgovora koji se vežu za jednu anketu „anketa_id = 1“. Model Servis „Anketa“ ima četiri modela: 1. odgovore možemo pisati na više jezika. answers – u tabelu „answers“ smještamo id (primarni ključ tabele). id 1 2 3 4 rezultat 15 10 20 8 anketa_id 1 1 1 1 5.1. tj. checkip – u tabelu „checkip“ smještamo id (primarni ključ tabele). jedan odgovor može imati samo jedan rezultat. rezultati_id povezuje sa tabelom “results” u odnosu 1:1. MVC servisa 7. results – u tabelu „results“ smještamo id (primarni ključ tabele). IP adresu korisnika. question. 7. Na osnovu ove tabele provjeravamo da li je korisnik već glasao i poll_code. ukupan broj glasova za svaki odgovor.php class Question extends AppModel { var $name = 'Question'. služi za snimanje IP adresa. Tabela „checkip“ nije povezana sa drugim tabelama. Tabelu answers pitanja_id povezuje sa tabelom “questions” u odnosu 1:n. lang_id i pitanje 4. 41 . odgvore (odgovor). rezultat tj. 3. lang_id povezuje tabelu “answers” sa tabelom ”languages” u odnosu 1:n. questions – u tabelu „questions“ smještamo id (primarni ključ tabele).

result.php class Answer extends AppModel { var $name = 'Answer'. answer.php class Result extends AppModel { var $name = 'Result'. } 3.php <tr> <td colspan="3"><table width="100%" border="0" cellspacing="10" cellpadding="0"> <tr> <td><span class="naslov_kategorije"><strong>Kreirati:</strong></span><br /> 42 .php class Poll extends AppModel { var $name = 'Poll'. } 7. } 4. admin_index.} 2.2. poll. View Servis „Anketa“ ima view „polls“ koji se sastoji od četiri php file-a: 1.2.

?>" enctype="multipart/form-data" > <?php 43 .') ? ></td> <tr> <td><?php echo $html->submit('Save') ?></td> </tr> U admin_add.<a class="menu" href="<?php echo $rootPath ?>Polls/admin_add/"> [Dodati novu anketu]</a><br /></td> </tr> </table></td> </tr> Admin_index. dugmić „Save“.') ?> </td> </tr> <tr> <td>Broj odgovora:</td><td><?php echo $html->input('Poll/noq'. Zatim smo kreirali tabelu i koristili smo html helpere za unos naziva ankete i broj odgovora u anketi. Koristili smo html helpere za poruku u slučaju greške koje smo poslije stavili pod komentar jer nisu bili neophodni i html helper za snimanje (prosljeđivanje) tj.php file-u kreirali smo formu koja pomoću metode post unesene podatke šalje admin_add_qu. array('size' => '10'))?></td> <td><?php //echo $html->tagErrorMsg('Poll/noq. Kreiramo tabelu i u redove/kolone tabele dodajemo link zbog preglednosti na stranici. 'Name is required.'/Polls/admin_add_qu'.php file-u.php služi za ispis linka za dodavanje nove ankete. admin_add.admin_add_qu.'/Polls/admin_add_an'.php <tr> <td colspan="3"><form method="post" action="<?php echo $rootPath. Dodali smo i css class „naslov_kategorije“ za ispis „Kreirati“ i class „menu“ za link [Dodati novu anketu]. koji view i php file se poziva. 3. 2. u našem primjeru Polls/admin_add/. 'Br_odg is required. zatim pišemo putanju tj.php <tr> <td colspan="3"><form method="post" action="<?php echo $rootPath.?>" enctype="multipart/form-data" > <table width="100%" border="0" cellspacing="10" cellpadding="0"> <tr> <td><table> <tr> <td>Pitanje</td> <td><?php echo $html->input('Poll/name'. array('size' => '40'))?> <?php //echo $html->tagErrorMsg('Poll/ name'.

?></td> </tr> </table></td> <td bgcolor="#666666">&nbsp.</td> </tr> <tr> <td height="35" bgcolor="#666666">&nbsp. array('size' => '80'))?></td> <td bgcolor="#E1E1E1">&nbsp.gif" alt="" /></td> <td width="96%" class="atributi_p"><?php echo $lang['Language'] ['name'].</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp.</td> <td bgcolor="#E1E1E1">&nbsp.</td> <td bgcolor="#E1E1E1" class="naslov_kategorije">Pitanje:</td> <td bgcolor="#E1E1E1">&nbsp.</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp.$lang['Language'] ['code'].</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp.</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp. ?>. array('value' => $lang['Language']['id'])). echo $html->hidden('Pages_'.</td> <td bgcolor="#E1E1E1"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <?php $j =1.</td> <td width="9">&nbsp. ?>lang/<?php echo $lang['Language']['code'].'/poll_id'.foreach ($langs as $lang){ echo $html->hidden('Pages_'.'/question'. array('value' => $poll_id)).'/lang'.</td> <td bgcolor="#666666" class="naslov_kategorije"><table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="4%"><img src="<?php echo $imgPath. ?> <table width="700" border="0" align="center" cellpadding="0" cellspacing="0"> <tr> <td width="12" height="25">&nbsp.</td> <td bgcolor="#E1E1E1">&nbsp.$lang['Language']['code'].</td> <td bgcolor="#E1E1E1"><?php echo $html->input('Pages_'.</td> <td width="329">&nbsp.</td> <td bgcolor="#E1E1E1">Odgovori:</td> <td bgcolor="#E1E1E1">&nbsp. $lang['Language']['code'].</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp. foreach($pitanja as $pitanje){?> <tr> 44 .

?> </td> </tr> <?php $j++.admin_add_an.</td> <td width="454"></td> </tr> <tr> <td height="35" bgcolor="#666666">&nbsp.</td> </tr> <tr> <td height="25" bgcolor="#E1E1E1">&nbsp.<td width="5%"><?php print $j. tj.</td> <td bgcolor="#E1E1E1">&nbsp.'/result_id_'.$j. ?></td> <td width="95%"><?php echo $html->input('Pages_'.$j. zastavu pored naziva jezika. prvu foreach petlju koristimo da izlistamo sve jezike koji se nalaze u bazi.php napravili smo formu koja pomoću metode post unesene podatke prosljeđuje admin_add_an. ?></td> </tr> </table> </form></td> </tr> U file-u admin_add_qu..$lang['Language']['code']. echo $html->hidden('Pages_'. id tabele „languages“.php <tr> <td colspan="3"><table width="100%" border="0" cellspacing="10" 45 .</td> <td bgcolor="#E1E1E1">&nbsp. }?> </table></td> <td bgcolor="#E1E1E1">&nbsp. $lang['Language']['code'].</td> <td bgcolor="#666666"><?php echo $html->submit('Save'). Drugu foreach petlju koristimo da ispišemo pitanja i odgovore na svim jezicima. 4. array('size' => '80')).</td> </tr> </table> <?php }?> <table width="700" border="0" align="center" cellpadding="0" cellspacing="0"> <tr> <td width="12" height="25">&nbsp.'/answer_'. Imamo dvije foreach petlje. css class. array('value' => $pitanja[$j-1])).php file-u. html helper za snimanje/submit i html helper za unos/input..Koristimo html helper za skriveno „hidden“ polje za provjeru poll_id i coda za jezik. Tabeli smo u neke redove i kolone dodali boju pozadine.

zatim imamo link za DNC stranu.2.php. dodali smo u kolonu i poruku „Hellooooo!!!!“.cellpadding="0"> <tr> <td><h1>Hellooooo!!!!<br /></h1></td> </tr> </table></td> </tr> <tr> <td colspan="3"><a class="menu" href="<?php echo $rootPath ? >Polls/admin_add/"><h5>Povratak na stranicu za kreiranje ankete</h5i></a></td> </tr> <tr> <td colspan="3" align="center"><span class="copy">Copyright &copy. answers_controlller.dnc. u contoller-u pišemo funkcije koje se izvršavaju u view-u. } Kada kreiramo controller . 7. results_controller. 2007 <a href="http://www. Controller Servis „Anketa“ ima četiri controller-a: polls_controller.php.3. 1.php. dodamo funkcije koje su nam potrebne: 46 .php i questions_controller.ba" target="_blank">DNC Spinnaker</a></span></td> </tr> </table></td> </tr> U file-u admin_add_an.php Kreiranje controller-a: class PollsController extends AppController { var $name = 'Polls'. polls_controller.php kreirali smo tabelu i link za povratak na stranicu za kreiranje ankete.

'$polls_id')"). if ($this->data) { $n = $this->data.`noq`) VALUES('$polls_id'. $ret = $ret[0]. 'http://localhost/cake/img/'). $i++){ $id += $i. $pitanje_id[$i] = $id. $this->set('imgPath'. '0'. if($id == 0) $id = 1. 'http://localhost/cake/'). $id = $ret[0]['max(id)']+1.function admin_index() { $this->set('rootPath'. $id = $ret[0]['max(id)']+1. $ret = $this->Poll->query("SELECT max(id) FROM polls"). '0'. `anketa_id`) 47 . $ret = $ret[0]. } $this->set('langs'. $ret = $this->Poll->query("SELECT max(id) FROM results"). } Funkcija admin_index() služi za kreiranje varijeble za prikaz početne strane. koju zatim pozivamo u funkcijama kada trebamo prikazati početnu stranu. 'http://localhost/cake/'). $polls_id = $id. $this->set('imgPath'. `ukupno_odg`. $name=$n['Poll']['name']. $noq=$n['Poll']['noq']. $this->Poll->query("INSERT INTO VALUES('$id'.'$name'. odnosno loga i slika potrebnih za bolji dizajn strane. $this->Poll->query("INSERT INTO polls(`id`. $i<$noq.`name`. function admin_add_qu() { $this->set('rootPath'. $this->requestAction('/languages/getLanguages')). if($id == 0) $id = 1.'$noq')"). U našem primjeru kreiramo varijablu $rootPath. 'http://localhost/cake/img/'). results(`id`. u našem primjeru 'http://localhost/cake/' i kreiramo varijablu $imgPath za prikaz slika. for($i=0. `rezultat`.

$ret = $ret[0]. function admin_add_an() { $this->set('rootPath'. 'http://localhost/cake/img/').$i].$this->set('poll_id'. `lang_id`. `pitanja_id`.'{$podaci['poll_id']}'. $this->set('pitanja'. $result_id = $podaci['result_id_'. `pitanje`)VALUES('$questions_id'. while(isset($podaci['answer_'. if($id == 0) $id = 1. Ako postoje podaci uzima max id tabele polls i u tabelu polls smješta podatke tj. } $questions_id++. $this->set('imgPath'. Zatim uzima max id od „results“ i prolazi kroz for petlju onoliko puta koliko ima odgovora u anketi i u tabelu „results“ unosi rezultat tj. ukupan broj glasova za svaki odgovor i anketa_id da poveže odgovore sa anketom.$i])){ $odgovor = $podaci['answer_'.$i]. $id = $ret[0]['max(id)']+1. $questions_id = $id. if ($this->data) { $ret = $this->Poll->query("SELECT max(id) FROM questions"). '$questions_id'. `lang_id`.'{$podaci['question']}')"). $pitanje_id). ime ankete i broj odgovora. '{$podaci['lang']}'. 'http://localhost/cake/'). } } 48 . `rezultati_id`) VALUES('$odgovor'. `anketa_id`. $polls_id). $i = 1. foreach($this->data as $podaci){ $this->Poll->query("INSERT INTO questions (`id`. '{$podaci['lang']}'. '$result_id')"). } } Funkciaj admin_add_qu() provjerava da li postoje podaci. $i++. $this->Poll->query("INSERT INTO answers (`odgovor`.

Funkcija admin_add_an() provjerava da li postoje podaci. na stranu gdje se nalazi anketa.lang_id = '$id'"). prolazi kroz foreach petlju dok postoje podaci. function ukupnoGlasaova($id ){ $page = array ('id' => $id). id jezika i pitanje u tabelu „questions“. } function inc($id) { $page = array ('id' => $id). 2. upisuje id ankete. id jezika. a. id pitanja i id rezultata upisuje u tabelu „answers“.rezultat. function rezultati($id ){ return $this->Poll->query("SELECT r. } Funkcija rezultati() vraća broj glasova za svaki odgovor u anketi. $ukupno = $this->Poll->findAll($page). Varijabla $i se inkrementuje onoliko puta koliko imamo odgovora i $questions_id onoliko puta koliko ima jezika u bazi.php class ResultsController extends AppController { var $name = 'Results'.rezultati_id = r. a odgovore.odgovor FROM answers a. Ako postoje podaci uzima max id iz tabele „questions“. results_controller. 49 . } Funkcija ukupnoGlasaova() vraća ukupan broj glasova i ispisuje ih na indexnu stranu.id AND a. return $ukupno[0]['Poll']['ukupno_odg']. results r WHERE a. tj.

"glasao"). function provjeraGlasanja(){ $ip_ = getenv ("REMOTE_ADDR"). tj. $ip_ = getenv ("REMOTE_ADDR"). `poll_code`) VALUES ('$ip_'. ' ')"). $this->Result->query("UPDATE `polls` SET `ukupno_odg` = `ukupno_odg` + 1 "). else $uslov1 = true. else $uslov2 = true.$rez = $this->Result->findAll($page). } Funkcija inc() inkrementuje broj odgovora i ukupan broj glasova. Pomoću funkcije getenv() uzima ip adrese korisnika i snima ih u tabelu “checkip“ i postavlja cookie ako je korisnik glasao. if($this->Result->query("SELECT poll_code FROM checkip WHERE IP = '$ip_'")) $uslov1 = false. $rez_id = $rez[0]['Result']['id']. if(isset($_COOKIE['DNCCMSANKETA'])) $uslov2 = false. $this->Result->query("INSERT INTO checkip (`IP`. $this->Result->query("UPDATE `results` SET `rezultat` = `rezultat` + 1 WHERE id = '$rez_id'"). 50 . setcookie("DNCCMSANKETA". vrši update tabele results i polls.

'http://localhost/cake/').php <?php class AnswersController extends AppController { var $name = 'Answers'. else return false. questions_controller. function admin_index() { $this->set('rootPath'. prikazuje reultate ankete. U našem primjeru anketa_id = 1.pitanja_id AND a. land_id = 1 i pitanje_id u tabeli „answers“odgovara id-u u tabeli „questions“. Ova funkcija se poziva u /pages/display. questions q WHERE q. 3. a. pitanje_id i land_id.lang_id = 1'). } Funkcija provjeraGlasanja() provjerava da li postoji korisnikova IP adresa u bazi i da li ima cookie. answers_controller. 4. a ako ne prikazuje anketu i korisnik ima mogućnost glasanja.id = a.rezultati_id FROM answers a. } } ?> Controller answers ima samo funkciju odgovori() koja vraća odgovore i rezultati_id koji su povezani preko anketa_id.anketa_id = 1 AND q.php file-u. } function view($id = null) { 51 . function odgovori() { return $this->Answer->query('SELECT a. $this->set('imgPath'. 'http://localhost/cake/img/'). Ako ima IP ili cookie ili oboje.php class QuestionsController extends AppController { var $name = 'Questions'.if(($uslov1) && ($uslov2)) return true.odgovor.

editovanje i brisanje ankete. $pitanje = $this->Question->findAll($pitanje). 'lang_id' => $lang_id). 3. funkcija view() služi za prikaz pitanja na osnovu id-a. return $pitanje[0]['Question']['pitanje']. Na admin strani kliknemo na link anketa 52 . $this->set('questions'. } Funkcija pitanje() služi za ispis pitanja na indexnoj strani.} } $this->Question->id = $id. poziva se u /pages/display. unosi naziv ankete i broj odgovora. Administrator kreira anketu. Upustvo za servis Servis „Anketa“ sa aspekta adminstracijskog dijela treba da omogući kreiranje. $this->Question->read()). function pitanje($id = 1.php file-u. 7. zatim pitanje i odgovore. 1. $lang_id = 1) { $pitanje = array ('anketa_id' => $id. Funkcija admin_index() ima istu funkciju kao u controller-u polls.

anketa br. Zatim se otvara nova strana sa linkom za dodavanje nove ankete 3. Admin unosi naziv ankete npr.2.1 i broj odgovora u anketi 53 .

4. na sve jezike koje ima u bazi 54 . Admin unosi pitanje i odgovore na više jezika tj.

55 .

Ako nema u bazi IP adrese i korisnik nema cookie-a. korisniku se prikazuju rezultati ankete Ključne mogućnosti servisa: . prikažu se rezultati glasanja 4. Servis je implementiran na 3 jezika (bosanski. nakon klika na dugmić “glasati“. Korisnik posjećuje stranu 2.pregled rezultata ankete Aplikacija dozvoljava da korisnik može samo jednom glasati na anketi na osnovu IP adrese i cookie-a. prikazuje mu automatski rezultat ankete. dozvoljava se glasanje.kreiranje ankete . Ako postoji IP adresa ili cookie. njemački) i ima mogućnost dodavanja novi jezika. Servis treba biti dostupan 24h svih sedam dana u 56 . Ako korisnik pokuša drugi put glasati.glasanje . engleski.Sa aspekta korisnika: 1. Provjerava se korisnikova IP adresa i cookie 3.

kreiranje ankete Kreirati anketu da bi dobili informacije od korisnika Visok Admin Server mora biti online 1. potrebno je osnovno poznavanje rada na računaru ( za kriranje ankete) i mogućnosti i znanje korištenja interneta ( za glasanje i pregled rezultata) Kategorije korisnika: obični korisnici (imaju mogućnost glasanja i pregleda rezultata) administratori ( imaju mogućnost kreiranja ankete) CASE: Naziv: Cilj: Prioritet: Grupa korisnika: Preduslovi: Scenario: CASE1. Admin kreira anketu na admin dijelu 2.sedmici.glasanje Glasati na anketi Visok Gost 57 . Servis nam treba omogućiti da dobijemo mišljenje korisnika o anketi „temi“ koju postavimo. Izvještaji: prezentacijski dio administracijski dio Usability: Lakoća korištenja servisa. Anketa je prikazana na prezentacijskom dijelu Naziv: Cilj: Prioritet: Grupa korisnika: CASE2 .

za pregled treba kliknuti da dugnić „Pregled rezultata“ 3. Gost posjećuje stranicu 2. korisnik mora biti online. gost Server mora biti online. gost mora biti online. Provjera gostove IP adrese i cookie-a. Ako prvi put glasa. Posjećuje stranicu 2. automatski se prikazuju rezultati Za admina: 1. Na osnovu IP adrese i cookie-a daje se mogućnost glasanja ili pregleda rezultata Naziv: Cilj: Prioritet: Grupa korisnika: Preduslovi: Scenario: CASE3 – pregled rezultata Pregledati rezultate glasanje ankete Visok Admin. 3. posjetiti stranicu.Preduslovi: Scenario: Server mora biti online. Gost posjećuje stranicu 2. Analizira rezultate ankete Dokumentacija seminarskog: osnove CakePHP-a opis aplikacije MVC za servis „Anketa“ case-ovi servisa „Anketa“ 58 . Ako je već glasao. posjetiti stranicu za pregled rezultata Za gosta: 1. da nije prije glasao 1.

xcache. ajax. CakeAMFPHP za vezivanje na flash. stabilnost.a uskoro bi trebao dobiti podršku za I18 arhitekturu. Jedini nedostatak koji sam pročitala tokom istraživanja za seminarski je što se do sada core cakephp mijenjao nekoliko puta.ZAKLJUČAK CakePHP je framework koji ima podršku za puno stvari kao što je memcache..višejezičnost.. brute force protection. CakePHP developri rade na razvoju preko 100 projekata. no s verzijom 2. ubrzanje razvoja.... Cakephp ima napravljene komponente za autentifikaciju.. Primjenom frameworka se dobiva mnogo prednosti: portabilnost.0 bi se to trebalo stabilizirati. Svi MVC framework-ovi nastoje zadovoljiti iste ciljeve i u MVC filozofiji su pravila jasna. html helperi. bez gubljenja fleksibilnosti. tako da ni implementacija ne može biti toliko različita. MVC framework možemo iskoristiti za većinu aplikacija u PHP-u koje želimo na brzinu iskodirati i time sami sebi uštediti vrijeme i natjerati se da se držimo nekih standarda.… . MVC arhitektura pomaže sa čistim predstavljanjem database funkcijalnosti. automatic layout switcher.Primarni cilj seminarskog je bio predstaviti strukturu frameworka koji omogućava PHP korisnicima svih nivoa brzi razvoj web aplikacija. biznis logike i prezentacije. 59 . pagination.

org http://bakery.org www. 3. 2.org www.LITERATURA 1. RI.forgephp.wikipedia. CakePHP Manual PHP Design Patterns ETF Sarajevo. 4.cakephp. SPIS 2006 Cakesheet Web stranice: www.cakephp.org www.org 60 .mi3dot.

Sign up to vote on this title
UsefulNot useful