# Yii - 2021 # Inleiding In *deze les gaan we leren wat een framework is en waarom we Yii gaan leren.* *We leren verder wat MVC en CRUD is en wat heeft dat te maken heeft met een framework.* Op het examen, kerntaak 2 moet je een webapplicatie bouwen. Je moet zelf weten hoe je dat doet. De meeste studenten kiezen voor PHP. Waarom? Het is relatief eenvoudig en de meeste web applicaties zijn ontwikkeld met PHP ([bron](https://w3techs.com/)). Veel websites lijken op elkaar en doen vaak dezelfde dingen. Ze maken een verbinding naar de database, laten gegevens zien, je kunt aanloggen, je kunt soms gegevens veranderen en ga zo maar door. Om niet telkens al deze 'standaard' zaken van 'scratch' af aan te moeten bouwen zijn er zogenaamde frameworks ontwikkeld. Elk framework is eigenlijk veen verzamelingen bouwblokken die je telkens op nieuw kunt gebruiken voor jouw website. Alle frameworks zijn net een beetje anders, maar er zijn ook veel overeenkomsten. Wat zijn belangrijke zaken als je voor een framework kiest: 1. Wordt het framework actief onderhouden; bestaat het al wat langer? Je wilt niet in iets gaan investeren dat volgend jaar misschien niet meer bestaat. 2. Hoe moeilijk is het om een framework te leren; wat is de complexiteit? 3. Hoe snel kun je iets ontwikkelen met het framework? 4. Hoe flexibel is een framework; kun je er echt alles mee maken? Bij punt 3 en 4 is het vaak het een of het ander. Of je kunt heel snel iets bouwen en je bent niet zo flexibel, of je kunt er eigenlijk alles mee ontwikkelen maar het kost veel tijd om dat te doen. Bijvoorbeeld: PHP is super flexibel je kunt er echt alle mee ontwikkelen, het Laravel framework is ook redelijk flexibel, het Yii framework is iets minder flexibel en als je naar bijvoorbeeld WordPress kijkt (wat je ook als een soort framework zou kunnen zien), dan is dat het minst flexibel. Aan de andere kant kun je met WordPress heel snel een site maken, je bent alleen een beetje beperkt in de functionaliteiten. #### Waarom Yii? Laravel is het meest gebruikte PHP-framework. Yii staat in de meeste lijstjes ongeveer op plaats 7. Dat komt, omdat je weliswaar "snel even wat kan bouwen", maar je kunt het vrij lastig alles precies zo maken als jij het wilt. Het is minder flexibel, maar biedt wel veel structuur. Als je straks je examen maakt dan kun je waarschijnlijk ongeveer 70%-80% van je code maken met Yii, daarvoor hoef je heel weinig te programmeren en heb je tijd om je te storten op de overige 20%-30%. De examens die jullie gaan doen lenen zich goed om met Yii te maken. Je bent namelijk redelijk vrij in het ontwerp en kunt dus de 'Yii-manier' aanhouden. Maar wat dan als ik later verder wil met (bijvoorbeeld) Laravel? Dat kan en Yii en Laravel hebben dezelfde opzet, MVC. Je zult in Laravel dus veel dingen terugvinden die je in Yii hebt geleerd. #### MVC? MVC, wat is dat? MVC staat voor Model, View en Control. Deze drie-deling wordt gemaakt in je code, zodat je eenvoudiger code kunt terugvinden.
ModelHierin staat waar de verbinding tussen jouw code en de database beschreven
ViewDit is de front-end en is vooral HTML/CSS (bootstrap) met heir een daar een 'vleugje' PHP
ControlDit is waar je bepaald wat er gebeurt, als je op een knop drukt ergens dan wordt de code uit de bijbehorende control uitgevoerd.
In een plaatje ziet dat er als volgt uit. De gebruiker doet wat en stuurt daarmee een verzoek (request) naar de juiste controller. De controller 'overlegt' met het model als er iets met data moet gebeuren en het resultaat wordt via de view aan de gebruiker getoond. [![image-1594413597727.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594413597727.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594413597727.png) In de komende lessen komen deze begrippen terug. Als het nu allemaal ngo wat onduidelijk is, geen probleem we gaan er mee aan de slag en zullen snel een gevoel krijgen wat MVC precies is en hoe het werkt. #### CRUD? Nog even....waar staat CRUD ook alweer voor? CRUD staat voor **C**reate, **R**ead, **U**pdate en **D**eleten. In een CRUD applicatie kun je dus gegevens aanmaken (create), lezen (read), bewerken (update) en verwijderen (delete). In het examen (Kerntaak 2) moet je minimaal een CRUD kunnen maken. Yii heeft een CRUD generator en je kan zonder te coderen een standaard CRUD applicatie maken. Super handig dus voor het examen. In de volgende les gaan we hier gelijk mee aan de slag. ### Opdrachten ##### Opdracht 1 Leg uit wat een ontwikkel framework is. Je mag hierbij gebruik maken van internet, maar beschrijf wel in je eigen woorden wat jij denkt dat een framework is. Bespreek met elkaar waarom jij een framework zou willen leren? Welke PHP frameworks ken je? ##### Opdracht 2 Als jij een framework kiest , wat is dan belangrijk voor jou? Zet de volgende punten op volgorde van belangrijkheid (van meest belangrijk naar minder). - Veel over te vinden (tutorials e.d.). - Veel plugins voor beschikbaar. - Wordt actief onderhouden, veel updates dus. - Is makkelijk te leren, kun je makkelijk je exmaen mee halen. - Kun je snel iets mee maken/ontwikkelen. - Heeft heel veel mogelijkheden. - Genereert automatisch code, die je dan kan aanpassen. - Heeft veel ‘straight out of the box’, sorteren, selecteren, login, menu, etc. - Is hip/modern/cool. \-- # 1, De eerste web app *In deze les gaan we alles klaarmaken en installeren voor het werken met Yii. Aan het eind van de les heb je werkende Yii CRUD-applicatie.* Een CRUD-applicatie is een applicatie waarmee je records kunt aanmaken (Create), records kunt lezen (Read), records kunt aanpassen (Update) en records kunt verwijderen (Delete). We gaan drie stappen doen om een werkende web app te maken: 1. installeren van alle software 2. maken van een eenvoudige database 3. generen van code In drie stappen hebben we straks een *werkende* web app in PHP gemaakt. Voordat je met deze les begint weet je hoe je met [phpMyAdmin](http://localhost/phpmyadmin/) (of ander tool) een database kunt maken en hoe je een .sql file kan importeren. Ook heb je XAMPP geïnstalleerd. ### Installatie ##### XAMPP Je hebt [XAMPP](https://www.apachefriends.org/index.html) geïnstalleerd. Voor Yii hebben dit nodig, omdat we een database gaan gebruiken. Heb je nog geen XAMPP of werkt die niet meer dan kun je [hier](https://www.roc.ovh/books/databases-voor-ict-beheer/page/xampp-installeren) lezen hoe je XAMPP kan installeren. ##### Composer Zorg er voor dat je [composer](https://getcomposer.org/) hebt geïnstalleerd. Composer is de installer van de WEB tools. Als we een framework als Yii of Laravel gaan gebruiken dan installeren we eigenlijk een hele grote doos met allemaal bouwblokken. Deze blokken moeten allemaal samen werken en zijn vaak afhankelijk van elkaar. De blokken worden door verschillende mensen ontwikkeld en op één of andere manier moet er voor worden gezorgd dat de juisten blokken zijn geïnstalleerd. Hiervoor dient Composer. Composer is als het ware de installer van PHP tools, deze worden vaak libraries of packages genoemd. Librabries zijn dan de blokken functionaliteit die je kunt gebruiken om een programma te maken. [https://getcomposer.org/](https://getcomposer.org/) Tip: Zorg dat XAMPP al in geïnstalleerd voordat je composer installeert; composer heeft namelijk de locatie van php.exe nodig. In deze korte video wordt getoond hoe je Composer kunt installeren: ##### Create new yii Project We maken het Yii project **world** aan. Je hoeft niet zelf een map/folder aan te maken. Dit doet composer voor jou. Zorg ervoor dat je in de terminal in de parent-folder bent. Vul de volgende code in de terminal: ``` composer create-project --prefer-dist yiisoft/yii2-app-basic world ``` Let op dat het nieuwe Yii project wordt gemaakt in de folder waarin je de cmd box opent. Stel je wilt je jouw nieuwe project op je Desktop maken dan open je een cmd box en je tikt het commando `cd Desktop` in. Voer dan het composer commando uit (zoals hierboven aangegeven) en het nieuwe Yii project zal op je Desktop worden gemaakt. ##### Start Development Server yii heeft een ingebouwde php server als je die opstart dan draait jouw website op [http://localhost:8080/](http://localhost:8080/) Let wel dat je XAMPP ook blijft draaien voor je database. In VCS open je jouw Yii project en daarna open je een nieuwe terminal. [![image-1618344180761.png](https://www.roc.ovh/uploads/images/gallery/2021-04/scaled-1680-/image-1618344180761.png)](https://www.roc.ovh/uploads/images/gallery/2021-04/image-1618344180761.png) In de terminal type je dan het commando in: ``` php yii serve ``` De Yii webserver draait nu. Ga naar je browser en controleer of op je via localhost:8080 jouw nieuwe Yii project ziet. Wil je cmd box gebruiken of lukt dit niet. Kijk dan naar de [instructiefilm](https://youtu.be/5idkjTWIL7g). ##### Web.php We moeten nu nog het een en ander configureren in Yii. We willen dat Yii zoveel mogelijk voor ons regelt. Om te beginnen mag Yii de routing regelen (dit wordt uitgelegd in de [volgende les](https://www.roc.ovh/books/yii/page/2-routing)). Open **config/web.php** - en zet het gedeelte dat urlmanager heet, uit commentaar (uncomment prettyURL). Het moet er als voglt uitzien (ongeveer op regel 50 van config/web.php). ``` 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ ], ], ``` Dit is een instelling die de [routing](https://www.roc.ovh/books/yii/page/2-routing) (waarover later meer) eenvoudiger maakt. ##### Database Let er op dat je **database goed is gedefinieerd** en dat je ook de relaties heb vastgelegd. Dat kun je doen met [phpMyAdmin](http://localhost/phpmyadmin/). De ingebouwde[ Yii CRUD-generator](http://localhost:8080/gii/crud) werkt alleen goed als de database en de relaties goed zijn vastgelegd. Voor onze eerste Yii web-app gaan we gebruik maken van de world database. In [deze les](http://www.roc.ovh/books/databases-voor-ict-beheer/page/1-wat-is-een-database#bkmrk-world) staat beschreven hoe je deze database kunt installeren. In deze les staat de [SQL-file world.sql](https://www.roc.ovh/attachments/53) waarmee je de database World kan maken (klik op de link of kijk aan de linkerkant van deze pagina). We gaan Yii vertellen welke database er moet worden gebruikt. We openen de file **config/db.php** en zetten daar het volgende in: ``` return [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=127.0.0.1;dbname=world', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ]; ``` Let op dat je geen localhost gebruikt om naar je database te verwijzen. Gebruik 127.0.0.1. ### Easy Debugging (optioneel) Om straks eenvoudig te kunnen debuggen maken we een paar aanpassingen. Voor nu mag je dit ook even overslaan, we kunnen dit later ook nog doen. Als je dit uitvoert dan kun je later in de code de volgende twee functies gebruiken: `d($objectnaam)` bekijk $object de code loopt gewoon door. `dd($objectnaam)` bekijk $object de uitvoering van de code stopt. Hoe krijg je dit? Eenvoudig twee stappen. Stap 1, New file config/functions.php ```PHP '; echo 'File: '.$caller['file'].''; echo ''; yii\helpers\VarDumper::dump($var, 10, true); echo ''; } /** * Debug function with die() after * dd($var); */ function dd($var) { $caller = debug_backtrace(1)[0]; d($var,$caller); die(); } ``` Stap 2, Edit config/web.php - voeg regel 3 en 4 toe. ``` Klik links op 'Model Generator' en type de naam van de databasetabel **country** in: [![image-1594285274135.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594285274135.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594285274135.png) De naam van het model wordt automatisch aangemaakt **Country**. Let op dat de 'Model Class Name' met een **hoofdletter** begint; kijk goed naar het voorbeeld. De naam van een class (oop) wordt altijd met een hoofdletter geschreven. Druk onderaan op de pagina op *preview*, Yii laat nu zien welke file hij gaan aanmaken voor je. Druk op **generate** om de file te maken. De file **model/country** is nu aangemaakt, we gaan later meer in op de inhoud van deze file. Wat voor nu belangrijk is om te onthouden is dat de model/country.php file de verbinding is tussen de tabel country en Yii. Elke keer als Yii informatie uit de country tabel nodig heeft dan gebruikt Yii de model/country.php file Omdat er vanuit de country tabel wordt verwezen naar de andere twee tabellen, **City** en **Countrylanguage** maak je nog twee modellen van deze twee tabellen. In je model directory (folder) heb je nu dus de volgende files staan: ``` City.php ContactForm.php Country.php Countrylanguage.php LoginForm.php User.php ``` ##### 2. CRUD compleet maken met controllers en views Ga terug naar de component builder, [open ](http://localhost:8080/gii)[http://localhost:8080/gii/model](http://localhost:8080/gii/model) Klik links op 'CRUD Generator' en type het volgende in: [![image-1618227641051.png](https://www.roc.ovh/uploads/images/gallery/2021-04/scaled-1680-/image-1618227641051.png)](https://www.roc.ovh/uploads/images/gallery/2021-04/image-1618227641051.png) Let goed op de hoofdletters, deze zijn belangrijk. Het View path wordt leeg gelaten, daar kan Yii zelf iets voor kiezen. Druk op preview, je ziet 8 files die klaar staan om te worden aangemaakt. [![image-1618227971123.png](https://www.roc.ovh/uploads/images/gallery/2021-04/scaled-1680-/image-1618227971123.png)](https://www.roc.ovh/uploads/images/gallery/2021-04/image-1618227971123.png) Druk op **generate** om de files aan te maken. ##### Controller In de directory controllers staat een file CountryController.php. Dit is de controller van de country CRUD. Al het denkwerk dat nodig is om de country CRUD aan te sturen zit in de CountryController.php file. ##### View In de views/country directory staan de views. De index.php is de standaard view en deze file bevat de (voornamelijk HTML code) om de pagina weer te geven. Weten we nog waar de controller en view ook alweer vandaan komen? Het Framework is gebouwd volgens de MVC-architectuur. In stap 1 hebben we het model gemaakt en in deze 2de stap hebben we de view en controller gemaakt. [![image-1612557541726.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1612557541726.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1612557541726.png) (deze sheet is uit de vorige les) De controller is waar alle verzoeken van de gebruiker naar toe gaan. De view is de presentatielaag met voornamelijk HTML, CSS, en JavaScript. En in het model wordt de verbinding met de database gemaakt. ### Klaar Ga naar [http://localhost:8080/country](http://localhost:8080/country) [![image-1594288457454.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594288457454.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594288457454.png) Je hebt een CRUD van Country gemaakt, compleet met search en sort-functionaliteiten. Je hebt ook een mooie pagina selector waarmee je daar de verschillende pagina's kan navigeren. Je kunt country's lezen/tonen, aanpassen, verwijderen en aanpassen. Probeer het maar! Wees niet bang om de database 'kapot' te maken want je kunt in een paar tellen de database opnieuw importeren. In de volgende lessen gaan we de automatisch gebouwde applicatie stap-voor-stap aanpassen. Voor nu kan je trots zijn op je eerste (?) web CRUD applicatie. #### Extra hulp met Filmpje Niet gelukt? Kijk in dit filmpje nog een keer alle stappen door: [Instructie Yii - les1](https://youtu.be/6NRMaJgSmLw) ### Opdracht Maak voor de tabellen: - city - countrylanguage ook een CRUD met de Crud Generator Controleer op [http://localhost:8080/city](http://localhost:8080/city) en [http://localhost:8080/country-language](http://localhost:8080/countrylanguage) of de CRUD werkt. ### Plus opdrachten 1. Ga in de code-editor naar de map **views** en probeer te vinden waar het overzicht Countries is gemaakt. Haal code weg zodat de kolom *Surface Area* niet meer wordt getoond. [![image-1612558210976.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1612558210976.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1612558210976.png) 2. Kun je de in dezelfde view de groene knop 'Create Country' verplaatsen en onderaan de pagina plaatsen? Dus het moet er zo uit te komen zien: [![image-1612558884647.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1612558884647.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1612558884647.png) \-- # 2, Routing *In deze les leer je wat routing is en hoe je binnen Yii de routing kunt instellen.* ### Wat is routing? (Algemeen) Routing is de manier waarop de webserver en jouw programma weet welke pagina er moet worden getoond. Kijk maar eens goed naar de URL in je browser. Je kunt de URL opdelen in 5 stukjes: [![image-1594292626981.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594292626981.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594292626981.png)
OnderdeelWat is het?Uitleg
Schemeprotocolhoe wordt de informatie verzonden (http, https, ftp, smb, file, ...)
domain namedomeindomein verwijst naar een server of een groep servers.
file pathpathverijst naar de file die de webserver moet 'uitvoeren'
parametersvariabelenhiermee kun je informatie naar de webserver sturen.
anchorverwijzing binnen de paginahiermee kun je naar een onderdeel van de pagina springen. Voorbeeld: https://www.roc.ovh/link/302#bkmrk-nog-een-keer-in-sche
Het schema bepaald hoe de informatie wordt opgevraagd (bij een webserver is dit meestal http en https). De domain name is de naam van de server(s). Het file path verteld de server welke informatie er moet worden getoond. De parameters zijn variabele die mee kunnen worden gegeven bijvoorbeeld om waarden die een gebruiker heeft ingevoerd naar de webserver te sturen. Tot slotte is het anchor (weinig gebruikt) een verwijzing naar een stukje binnen een pagina. Normaal gesproken komt het file path overeen met de locatie van de bestanden op de server. Het file path is het path vanaf de document root. Stel de document root staat op jouw laptop naar: c:\\www Je hebt XAMPP opgestart en je gaat naar: [http://localhost/opgave1/uitslag/index.php](http://localhost/opgave1/uitslag/index.php) Dan wordt de file c:\\www\\opgave1\\uitslag\\index.php door de webserver uitgevoerd. Het file path wordt dus achter de *document root* geplaatst #### Hoe kom je van de URL in je browser naar de file op jouw laptop? [![image-1613336806883.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1613336806883.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1613336806883.png) Met routing zorg je ervoor dat je ergens een vertaling wordt gemaakt tussen het URL en de file PHP die moet worden uitgevoerd. Alles wat hierboven is beschreven is de *standaard* routing. Zo werkt het op jouw laptop als je plain PHP gebruikt (dat is PHP zonder framework). Routing zorgt ervoor dat het path en eventueel de parameters de juiste code uitvoeren. Bij eenvoudige web applicaties is het path gewoon de locatie van de file op de harddisk, maar voor meer ingewikkelde programma's kan dat al snel onoverzichtelijk worden. Met een framework werkt het daarom anders. ### Routing in Yii Standaard hoef je niets te doen voor routing. Er zit al heel veel voorgebakken in Yii. Stel je typt in je browser in localhost:8080/country/index Dit path bestaat uit twee delen; country en index. country verwijst naar controllers/CountryController en index verwijst naar de public function actionIndex in de class CountryController. Het path bestaat in Yii uit twee delen, de verwijzing naar de controller en de verwijzing naar de functie/method. De vertaling van path naar controller en function/method gaat als volgt: 1. het eerste deel van het path is de controller-naam, maar 1. de controller-naam begint altijd met een hoofdletter, en; 2. als er een streepje (-) in het path staat dan is dat een nieuw woord en in de controller begint dit nieuwe woord met een hoofdletter. Voorbeeld: dit-is-het-eerste-deel wordt DitIsHetEersteDeel 2. Het tweede deel van het path wordt opdezelfde manier vertaald maar er wordt ook nog eens het woord action voorgezet. Voorbeeld: dit-is-het-tweede-deel wordt actionDitIsHetTweedeDeel ##### Een paar voorbeelden:
pathfilepublic function
/kaart/overzichtKaartControlleractionOverzicht
/klas-lokaal/overzichtklasLokaalControlleractionOverzicht
/klas-lokaal/stoel-maatklasLokaalControlleractionStoelMaat
/klas-lokaal/indexklasLokaalControlleractionIndex
/klas-lokaalklasLokaalControlleractionIndex
Zoals is te zien in de laatste regel, wordt de actionIndex uitgevoerd als het tweede deel van het path ontbreekt. ### Oefening We gaan in onze World-applicatie oefenen met routing (1) Zet in de CountryController in de CountryController class een nieuwe public function: ``` public function actionHello() { echo "Hello World!"; exit; } ``` **Opdracht:** Save de aangepaste CountryController en bedenk met welke url je nu deze nieuwe functie (method) moet aanroepen. Controleer dat, zodat je in jouw browser het volgende ziet: [![image-1594296881002.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594296881002.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594296881002.png) (2) Maak een nieuwe file **controllers/ExampleController.php** en zet daar het volgende in. ```PHP ` dit is kort voor`` . Het toont jouw pagina die in de variabele $content staat: [![image-1614021559589.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614021559589.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614021559589.png) ### Nav Widget in main.php In de main.php in de folder lay-outs staat de standaard Yii pagina structuur. Het menu is hier ook een onderdeel van. In de main.php staat een stuk PHP-code die gebruik maakt van een Yii widget. Een Yii widget is eigenlijk een plug-in zoals we die ook bij WordPress kennen. De code voor de menu bar heeft de volgende structuur: ```PHP Yii::$app->name, // de naam van het menu 'brandUrl' => Yii::$app->homeUrl, // de home page waar je naar toe gaat als je op de naam klikt 'options' => [ 'class' => 'navbar navbar-expand-md navbar-dark bg-dark fixed-top', ], ]); echo Nav::widget([ // hier worden de menu's en menu items bepaald 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => [ [ 'label' => 'Country', 'items' => [ ['label' => 'Overzicht', 'url' => ['/country/index', ''] ], ['label' => 'Voeg toe', 'url' => ['/country/index', ''] ], ['label' => 'Europa', 'url' => ['/country/index', ''] ], ], ], ], ]); NavBar::end(); ?> ``` In Yii worden parameters voor Widget via assiociative arrays mee gegeven. Deze hebben de structuur: ```PHP [ 'key1' => 'waarde1', 'key2' => 'waarde2' ] ``` de waarde kan dan ook weer een assiociative array zijn: ```PHP [ 'key1' => 'waarde1', 'key2' => [ 'andere_key1'=> 'andere_waarde1', ........ ]  ] ``` ### Opgaven 1. Pas het menu aan en plaats de code zoals hierboven beschreven: [![image-1613993167595.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1613993167595.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1613993167595.png) 2. Om een country toe te voegen moet in de controller *CountryController* de method/function *actionCreate*() worden uitgevoerd. Bedenk hoe je de routing naar deze pagina opzet en pas het menu aan '*voeg toe*' zodat deze is gekoppeld aan *actionCreate*() van de *CountryController*. 3. In elk menu-item staat de route, bijvoorbeeld 'country/index' en daarna staat een ''. Deze laatste lege string zijn de parameters. Als we alle landen uit Europa willen filteren dan kunnen we de volgende URL gebruiken: [![image-1613993783698.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1613993783698.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1613993783698.png) Het gedeelte na de ? is de parameter. In Yii zet je een paramter als volgt in een assiociatieve array (element): 'countrySearch\[Continent\]'=>'Europe'
Het hele menu-item ziet er dus als volgt uit:
[![image-1615820099350.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1615820099350.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1615820099350.png)Pas je menu aan en test uit of alle drie de menu-items goed werken. 4. 5. Maak nu een extra menu-item en noem dat Asia. Dit menu-item laat alle landen van Asia zien. 6. Maak nu een menu-item voor alle werelddelen: North America, South America, Asia, Europe, Africa, Antarctica en Oceania. 7. Maak een extra menu city met als items 'Overzicht' en 'Voeg toe'. [![image-1614009669414.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614009669414.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614009669414.png) \-- # 4, Gridview Widget (view) *Dit In deze les leer je de basis aanpassingen in de Gridview maken.* *Je leert hoe je kolommen wel of niet **zichtbaar** kan maken, hoe je de kolom **headers** kan aanpassen, hoe je de kolom **breedte** kan aanpassen, hoe je de **inhoud** van de kolom zelf kan aanpassen en als laatste leren hoe we een **link** kunnen maken van een kolom.* ### Country overzicht aanpassen Onze CRUD-generator (Gii) heeft voor ons een country overzicht gemaakt. Als je de CountryController.php opent dan zie je dat function (method) actionIndex de view index aanroept. Open deze index file. Deze staat in views/country. ### Gridview Widget Je ziet dat er op regel 23 een zogenaamde widget wordt aangeroepen. Een Widget is een plug-in waarmee je functionaliteit aan Yii toevoegt. De Gridview Widget is misschien wel de meest uitgebreide widget en het zorgt ervoor dat we een mooi overzicht van onze data krijgen. #### 1. Kolommen aanpassen In de parameters van deze widget zie je dat de kolommen *Code*, *Name*, *Continent*, *Region* en *SurfaceArea* worden afgedrukt. Je ziet ook dat bepaalde kolommen niet worden afgedrukt deze staan in commentaar.
##### Opdracht 1 Verander de lay-out in het country overzicht en laat de volgende kolommen in deze volgorde zien: *Code*, *Name*, *Population*, *SurfaceArea* en *Capital*. *Population* laat inwoneraantal zien en *SurfaceArea* laat oppervlakte zien.
#### 2. Kopjes aanpassen Stel we willen het kopje boven de kolom veranderen, dan kan dan als volgt. Vervang bijvoorbeeld 'SurfaceArea' in de GridView door het volgende array. ``` [ 'label' => 'Oppervlakte', 'attribute' => 'SurfaceArea', ], ``` Je ziet dat we de surface area nu hebben vervangen door een assiociative array. Dus waar in de code het woord 'SurfaceArea' staat plaats je het assiociative array zoals hierboven is aangegeven. Dit verteld de GridView snippet dat het label Oppervlakte moet zijn (dit wordt boven aan de pagina gezet) en dat de waarde in de kolom moet worden gevuld met 'SurfaceArea'.
##### Opdracht 2 Pas nu de kolom *Population* aan; en zet in de kolom header 'Inwoners'.
#### 3. Kolom breedte aanpassen We gaan de kolombreedte aanpassen. Vervang het blok van net door het volgende: ``` [ 'label' => 'Oppervlakte', 'attribute' => 'SurfaceArea', 'contentOptions' => ['style' => 'width:30px; white-space: normal;'], ], ```
##### Opdracht 3 Pas de breedte van de kolom *Inwoners* (*Population*) ook aan; zet de breedte op 30 pixels.
#### 4. Waarde van kolom aanpassen Stel we willen km2 achter de oppervlakte plaatsen, dan kunnen we de waarde van de kolom als volgt aanpassen: ```PHP [ 'label' => 'Oppervlakte', 'attribute' => 'SurfaceArea', 'format' => 'raw', 'value' => function($data) { return sprintf("%8d k㎡", $data->SurfaceArea); } ] ``` Regel 4 t/m 7 in deze code verandert de waarde van de kolom.
##### Opdracht 4 Test uit wat de code doet, haal 'format'=> 'raw' weg en kijk wat er gebeurt. Waarom is deze regel nodig? Vervang regel 6 door ``` return 'hallo'; ``` en kijk wat er gebeurt.
#### 5. Link maken van een kolom In de Capital kolom zien we geen plaatsnaam, maar een nummer. Dit is het ID van een plaats in de city tabel.

Dus de *Capital* kolom uit *country* is de foreign key die wijst naar de primary key *ID* in *city.*

In de city view kun je een plaats naam zoeken door bijvoorbeeld de volgende URL te gebruiken: [http://localhost:8080/city/index?CitySearch\[ID\]=179](http://localhost:8080/city/index?CitySearch%5BID%5D=179) 179 is het ID van Brussels, de hoofdstad van België. We gaan nu de value van de kolom Capital vervangen door de link. Laten we om te beginnen de kolom *Capital* een beetje gaan aanpassen:
##### Opdracht 5 Vervang de kolomnaam van de kolom Capital door 'Hoofdstad'. Verander de breedte van deze kolom naar 30px. Vervang de value van de kolom in ``` 'value' => function($data) {

return "/city/index?CitySearch[ID]=179";

} ``` (alle kolommen krijgen dus dezelfde waarde).
Je ziet nu: [![image-1614025244735.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614025244735.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614025244735.png) Dat is nog geen link. Dat komt omdat we met <a href="">...</a> een link moeten maken. Dat kan maar dat is lastig omdat we met " in " quotes zitten. Dus dubbele quotes in dubbele quotes. Dat kan, maar het is een gepriegel. Met de Yii module Html kunnen we dit eenvoudiger doen. Zet eerst boven aan in de code: ``` use yii\helpers\Html; ``` En we vervangen nu de regel die de waarde returned in: [![image-1614026085274.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614026085274.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614026085274.png) We zijn er bijna, we moeten nu alleen de 179 nog variabel maken. [![image-1614026443516.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614026443516.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614026443516.png)
#### Opdracht 6 Maak je code werkend met bovenstaande code. Als je nu op het nummer klikt ga je naar de hoofdstad van het betreffende land. Verander nu de tekst van de link zodat je in de kolom Hoofdstad, in 'naar hoofdstad'. Als je er op klikt dan ga je dus naar de index view van de hoofdstad. Verander ook de breedte van de kolom naar 120px zodat de kolom netjes past.
De kolom ziet er dus als volgt uit: [![image-1614026822965.png](https://www.roc.ovh/uploads/images/gallery/2021-02/scaled-1680-/image-1614026822965.png)](https://www.roc.ovh/uploads/images/gallery/2021-02/image-1614026822965.png)
#### Opdracht 7 (a) Zet de kolommen in de volgende volgorde: Code - Name - Hoofdstad - Population - Oppervlakte (b) Verander de kolomnamen als volgt: Code - Naam - Hoofdstad - Inwoners - Oppervlakte (c) Zorg dat de naam van het land vet gedrukt wordt (bold). (d) Haal vervolgens de volgnummers (1,2,3,4....) weg en verplaats de edit kolom (de laatste) naar voren. Het hele overzicht ziet er uit zoals te zien is op het volgende plaatje:
[![image-1617127616569.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1617127616569.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1617127616569.png) Dit is het eindresultaat. ### Plus opdracht \*\* De bevolkingsdichtheid zegt iets over de hoeveelheid inwoners in een land. Natuurlijk heeft Amerika meer inwoners dan Nederland, maar het land is ook vele groter. Om te vergelijken hoe dichtbevolkt een land is, kun je uitrekenen hoeveel mensen er gemiddeld per km2 wonen. bevolkingsdichtheid = aantal inwoners / oppervlakte. Stel een land heeft een oppervlakte van 100 km2 en je er wonen 200 mensen. Dan heb je in dit land dus gemiddeld 2 inwoners per km2, want: 2 = 200 / 100 Maak nu een nieuwe kolom in het *country* overzicht en bereken daarin de bevolkingsdichtheid. \-- # 5, Menu - Controller - View *We gaan een eigen overzicht maken. We beginnen met het aanmaken van een menu, dan maken we een controller, en van de controller gaan we naar de view. We leren data op te halen met de controller en deze te tonen in onze eigen view.* *Het ophalen van data doen we met een eenvoudige query die we in de Controller plaatsen. Vanuit de Controller gaan we via het Model naar de database. De volgorde wordt:*
Gebruiker -> (kiest) menu -> controller -> model -> database -> (resultaat terug naar) controller -> view -> gebruiker.
*In deze les maken we dus het hele 'rondje'. Dit is een standaard 'rondje' dat in elke applicatie vaak terug zal komen.* ### Menu - Controller - View Om te beginnen maken we een nieuwe method (=functie) in de **controllers/CountryControlle**r class. Deze controller noemen we actionOverzicht. Volgens de routing regels wordt de route wordt dus /country/overzicht.
##### Opdracht 1 Maak een menu item *overzicht* onder country dat naar */country/overzicht* gaat. [![image-1614620329168.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614620329168.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614620329168.png)
Probeer het menu uit. Wat zie je en kun je verklaren wat je ziet? Juist.... je krijgt een foutmelding want de routering verwijst naar de method/function actionOverzicht in de controller CountryController en die bestaat niet! We gaan dus een stukje code toevoegen aan onze CountryController. ```PHP use yii\data\Pagination; .. .. public function actionOverzicht() { // dit is de query, dit is te vergelijken met select * from Country $countries=Country::find()->all(); // de view wordt aangeroepen en het object $countries en $pagination wordt meegegeven. return $this->render('overzicht', [ 'countries' => $countries, ]); } ```
##### Opdracht 2 Maak de function *actionOverzicht* in de *CountryController* zoals hierboven is aangegeven.
Test opnieuw wat er gebeurt als je het menu *overzicht* selecteert ![image-1614623472969.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614623472969.png) What? Weer een fout? Waarom nu? Juist het menu gaat naar de actionOverzicht en de actionOverzicht voert een query uit en roept de view overzicht op. En die view bestaat (nog) niet. We gaan dus in de folder views/country een nieuwe file overzicht.php aanmaken en plaatsen daar de volgende code in. ```PHP Name; echo " - "; echo $country->Code; echo " - "; echo number_format($country->Population, 0, ',', ' '); echo "
"; } ?> ```
##### Opdracht 3 Maak de view *overzicht* zoals hierboven is aangegeven.
Het overzicht is bijna niet geformatteerd. [![image-1614627165703.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614627165703.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614627165703.png) Weet je nog hoe een HTML table er uit ziet? ```HTML
.. .. ..
.. .. ..
``` Hierboven staat een skelet van een table met twee rijen en drie kolommen. Vind je dit lastig? [Kijk dan de video.](https://youtu.be/D00WkHqw2qk)
##### Opdracht 4 Verander de /view/country/overzicht nu zo dat het netjes in een table wordt gezet. De output komt er als volgt uit te zien: [![image-1614627534845.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614627534845.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614627534845.png) De kolommen staan nu dus netjes onder elkaar.
Je hebt nu zelf een view opgebouwd zonder gebruik te maken van de Gridview widget. Het kost meer tijd en moeite om zelf een overzicht zonder Gridview widget te maken, maar je hebt wel veel meer controle over hoe jouw overzicht er uit komt te zien. ### Sorteren en selecteren Via de Yii Gridview widget hebben we geleerd dat we eenvoudig kunnen sorteren en selecteren. De Yii Gridview heeft ook beperkingen. Je kunt bijvoorbeeld niet alle landen zoeken met *meer dan* X inwoners. Soms wil je ook dat de gebruiker zelf niet kan selecteren. Hij krijgt dan niet alle regels uit de tabel te zien, maar bijvoorbeeld alleen de regels met de datum van vandaag. We gaan het sorteren en selecteren via de controller coderen. Kijk nog eens naar de *CountryController* bij de *actionOverzicht*, daar staat: `$countries=Country::find()->all();` Dit statement kijkt naar het object *Country* dat in het model is gemaakt en zoekt dan naar alle regels uit dit object. Dit object wordt in het model gekoppeld aan de tabel *country*. Yii vertaald dit naar de query `SELECT * FROM country` We kunnen nog veel meer, kijk maar eens naar het volgende uitgebreide voorbeeld: `$countries = country::find()->where(['Continent' => 'Europe'])->orderBy(['Name' => SORT_ASC])->all();` Hier worden alle *countries* geselecteerd met *Continent*=*Europe*, gesorteerd op *Name.* Dit is een soort Yii query; een query in Yii-formaat. Het SQL-statement zou er zo uit zien: `SELECT * FROM country WHERE Continent = 'Europe' ORDER BY Name ASC` Op deze manier kun je dus via de code in de controller een selectie maken en de sortering aanpassen. In de volgende opdracht komt dit terug.
##### Opdracht 5 - - Maar een nieuw menu-item en noem dit *Overzicht Europe*. - Maak een nieuwe function *actionOverzichtEurope,* zet deze in de juiste controller en koppel deze aan het menu-item. (let op de vertaling naar de routering; in de routering staat nooit een hoofdletter en woorden worden gescheiden door een streepje "-" , [zie les 2 over routing](https://www.roc.ovh/link/302#bkmrk-een-paar-voorbeelden)). - Laat in dit overzicht alle landen van Europa zien. Gebruik hiervoor het stukje code dat hierboven staat ($countries = country::find()->where.........). - Druk de volgende kolommen af: *Name* en *Surface Area* - - Zet de twee kolommen netjes onder elkaar - Formateer de Surface Aera netjes met spatie tussen de duizendtallen en lijn ze recht uit. - Maak een header die **bold** is. - Sorteer het overzicht op Surface Area van grootste land naar kleinste. Het overzicht komt er dus zo uit te zien: [![image-1614629726011.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614629726011.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614629726011.png)
Je hebt geleerd dat je een overzicht kan maken via een (Yii-)query. Met de query kan je de selectie en sortering aanpassingen. De query wordt geplaatst in de *controller* en maakt gebruik van het model. In dit voorbeeld het model van *country*. Dit komt terug in het examen, daarom nog een opgave over dit onderwerp.
##### Opdracht 6 Pas het overzicht van Opdracht 5 aan zodat je alleen alle kleine Europese landen ziet. Een land is klein als `SurfaceArea < 100000` Pas hiervoor de controller aan. Je hebt dus te maken met **twee** zoekcriteria. Zoek zelf op internet uit hoe je in een 'Yii-query' twee zoekcriteria kan combineren met *andWhere().*
\-- # 6, Relaties 1:1 en 1:N *In deze les gaan we bekijken hoe je relaties legt tussen tabellen en hoe je informatie uit andere tabellen kan afdrukken in jouw view.* Een relatie in de view wordt altijd gelegd tussen één regel (row/record) van één tabel naar één of meer rijen in een andere tabel. Omdat de relatie vanuit één regel wordt gelegd, is er dus altijd sprake van een 1:1 of 1:N relatie. We gaan beide relaties apart bespreken en uittesten met voorbeelden. Als uitgangspunt nemen onze eigen view die we in de vorigen les hebben gemaakt. We gaan een kolom toevoegen waarin de hoofdstad van el land wordt getoond. [![image-1615887892142.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1615887892142.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1615887892142.png) ### 1:1 relatie Bekijk de *World* database. In de *country* tabel staat de kolom *Capital*, dit is een foreign key die verwijst naar de primary key *ID* in de *city* table. [![image-1615888192933.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1615888192933.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1615888192933.png) Vanuit de country table kun je via de foreign key Capital verwijzen naar de Name in de gekoppelde tabel city. ```PHP $country->hoofdstad->Name; ``` Met dit statement verwijs je vanuit de *country* tabel, via de relatie *hoofdstad* naar de N*ame* (naam) van de hoofdstad. Nu moet de relatie hoofdstad alleen nog worden geprogrammeerd. Dit doen we in de model file van Country. ```PHP // models/Country.php public function getHoofdstad() { return $this->hasOne(City::className(), ['ID' => 'Capital']); } ``` De functie *getHoofdstad* vertel jij Yii hoe de relatie tussen de tabel *Country* en *City* in elkaar zit. Een 1:1 relatie wordt dus op de volgende manier gemaakt:`` ```PHP public function get() { return $this->(::className(), ['' => '']); } ``` In een query zou de relatie er als volgt uit zien: ```SQL SELECT * FROM Country LEFT JOIN City ON Country.Capital = City.ID ``` De relatie is gemaakt in het Model van Country en is dan ook alleen vanuit Country Beschikbaar. Je kunt in een view van Country dan gegevens uit de relatie afdrukken door: `$country->relatie_naam['kolom naam']`
##### Opdracht 1 **Lees** de uitleg hierboven goed door en plaats de kolom hoofdstad in het overzicht. [![image-1615887892142.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1615887892142.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1615887892142.png) Maak hiervoor twee aanpassingen: 1. 1. maak de relatie in het model van *Country*, en 2. pas de view *overzicht* aan door daar de kolom *Hoofdstad* aan toe te voegen.
We gaan de Hoofdstad clickable maken. Als je naar [http://localhost:8080/city](http://localhost:8080/city) gaat dan zie je het overzicht van steden. Klik op een oogje op een van de steden. Let op de URL. Je ziet [http://localhost:8080/city/view?id=<NR>](http://localhost:8080/city/view?id=) als url en <NR> is het ID van de stad. Dus als je bijvoorbeeld klikt op [http://localhost:8080/city/view?id=5](http://localhost:8080/city/view?id=5) dan zie je informatie over Amsterdam.
##### Opdracht 2 Maak elke hoofdstad in het overzicht datje bij opdracht 1 hebt gemaakt *clickable. Als je op de link klikt dan* open je het overzicht van de stad, bijvoorbeeld [http://localhost:8080/city/view?id=5](http://localhost:8080/city/view?id=5). Tip: je kunt Html:a() gebruiken om een link te maken. Dit is in de [vorige les uitgelegd](https://www.roc.ovh/link/303#bkmrk-zet-eerst-boven-aan-). [![image-1615896595590.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1615896595590.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1615896595590.png)
### 1:N relatie In de *World* database staat een tabel *countrylanguage*. In deze tabel staat per land welke talen er worden gesproken. Per taal wordt ook aangegeven welk percentage van de bevolking deze taal spreekt. De relatie tussen country en *countrylanguage* is 1:N. Namelijk in een land worden **1 of meer** talen gesproken. We willen een overzicht maken dat er als volgt uitgaat zien: [![image-1594550267859.png](https://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594550267859.png)](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594550267859.png) In dit voorbeeld zien we dat in Indonesië 8 talen worden gesproken. We gaan ons overzicht aanpassen, zodat we per land alle talen die er worden gesproken netjes in de tabel worden weergegeven zoals hierboven in het voorbeeld is te zien. Hoe werkt dit? Bij de 1:1 relatie hierboven konden we via `$country->hoofdstad['Name']` de naam van de hoofdstad uit de gelinkte tabel opvragen. Als dit niet duidelijk is, lees dan het stuk over 1:1 relatie hierboven nog een keer goed door! Bij een 1:N relatie werkt het hetzelfde als bij de 1:1 relatie. Dus in het model (*models/Country.php*) s de relatie beschreven: ```PHP public function getCountrylanguages() { return $this->hasMany(Countrylanguage::className(), ['CountryCode' => 'Code']); } ``` Controleer of je het model hebt aangemaakt van *Countrylanguage* tabel. Let even op de hoofd en kleine letters!
tabel naam in databaseCountrylanguage
model naamCountryLanguage
relatienaam in Country.phpCountrylanguages
We kunnen nu vanuit de view (*views/country/overzicht.php*) via `$country->countryLanguages` de taal opvragen.

Als je de Gridview widget gebruikt dan noem je het veld (in de Widget) `country.countryLanguages`

Er is alleen één belangrijk verschil, omdat we geen 1:1 relatie hebben, maar een 1:N, krijgen we geen variabele terug maar een array. We moeten het dus met een loop door het array heen lopen. Dit gaan we demonstreren met behulp van de debug functie die we in [les 1 (optioneel)](https://www.roc.ovh/link/296#bkmrk-%C2%A0-1) hadden toegevoegd. als je dit nog niet hebt gedaan dan moet je dat nu doen. #### Debugging Ga nu naar je view en voeg bovenaan in de loop waarmee je het overzicht afdrukt, de regel dd($countries) toe. *dd* staat voor dump & die. Dus *dd()* laat de variabele zien en stopt met het uitvoeren van het programma. ```PHP "; .... foreach ($countries as $country) { ... // hier staat al code voor eerste kolommen ... // voeg hier deze kolom toe echo ""; d($country->countrylanguages); echo ""; ... } ... ``` Als we even de border van de tabel aanzetten (regel 3) dan is het iets makkelijker om de output te lezen. Als je goed kijkt dan zie je in de rechter kolom een array van objecten. In deze Yii-objecten staat telkens een 'Language'. [![image-1616504143444.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616504143444.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616504143444.png) Om de taal te af te drukken moeten we eerst het element array aanwijzen: ``` $country->countrylanguages[0] $country->countrylanguages[1] $country->countrylanguages[2] $country->countrylanguages[3] ``` En van elk element moeten we de 'Language' hebben. Dat kun je in de dump (plaatje hierboven) goed zien. ``` $country->countrylanguages[0]->Language $country->countrylanguages[1]->Language $country->countrylanguages[2]->Language $country->countrylanguages[3]->Language ``` Je weet natuurlijk niet hoeveel talen er in elk land worden gesproken. De lengte van het array is in dit voorbeeld 4, maar het kunnen er natuurlijk meer of minder zijn.
##### Opdracht 3 **Lees** de uitleg hierboven goed door. Druk in de laatst kolom één taal af. [![image-1616504705123.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616504705123.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616504705123.png)
Nu hebben we één taal per land, maar in de meeste landen wordt meer dan één taal gesproken. We gaan nu met een loop door het array $country->countrylanguages heen. Gebruik hiervoor de volgende loop. ```PHP foreach( $country->countrylanguages as $taal) { // vul zelf de jusite variable in om de taal af te drukken echo .....; echo "
"; } ```
##### Opdracht 4 Gebruik de loop zoals hierboven is aangegeven en maak het volgende overzicht. [![image-1616505080100.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616505080100.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616505080100.png) Zorg ervoor (met CSS) dat de tabel er netjes uitgelijnd uit ziet zoals hierboven in het voorbeeld.
Je hebt nu een 1:N (één op meer) relatie gemaakt. In volgende lessen gaan we hier opnieuw mee aan de slag.
##### Opdracht 5 In de Countrylanguages tabel staat naast de taal ook het percentage. Dit is het percentage van de bevolking dat deze taal spreekt. Zet dit percentage tussen haakjes achter elke taal. Voorbeeld: [![image-1616505413868.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616505413868.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616505413868.png)
Jammer dat de percentage niet gesorteerd zijn! Pas de relatie aan in het Country model ```PHP public function getCountrylanguages() { return $this->hasMany(CountryLanguage::className(), ['CountryCode' => 'Code']) ->orderBy(['Countrylanguage.Percentage' => SORT_ASC]); } ```
Regel 4 is erbij gekomen. let op de ; ! Als je nu het overzicht opnieuw toont dan zie je dat de talen nu oplopend (van laag naar hoog) zijn gesorteerd.
##### Opdracht 6 Sorteer de talen op percentage aflopend van hoog naar laag. Op die manier komt de meest gesproken van een land bovenaan. [![image-1616506111435.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616506111435.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616506111435.png) Tip: gebruik Google om te zoeken hoe je de relatie in het model verandert zodat je aflopend kunt sorteren.

Als je in de database de relaties (via SQL) goed definieert dan worden de relaties in Yii door de model generator automatisch gemaakt. Het aanmaken van relaties in de databases valt buiten deze lessen en kan lastig zijn. Het is dus belangrijk dat je snapt hoe de relaties tussen tabellen in Yii worden gemaakt.

--- # 7, en nu koffie! In deze les gaan we alles wat we tot nu toe hebben geleerd herhalen. We bouwen een nieuwe applicatie. In de volgende les gaan we nieuwe dingen leren met deze applicatie. ### Wat gaan we maken? We gaan een applicatie maken waarin medewerkers van een coffee-to-go een bestelling kunnen opnemen. De medewerker neemt een bestelling op, vraagt de naam van de klant. De bestelling krijgt een status *besteld.* De bestelling wordt klaar gemaakt en de barista (koffiemaker) zet de status van de bestelling op *klaar.* De medewerker ziet dat zijn bestelling klaar is, roept de naam van de klant en rekent af. De status wordt op *geleverd* gezet. ### Opdracht 1
##### Opdracht 1 Maak de *coffee* database met de drie tabellen, menu, medewerker en bestelling. Maak voor elke tabel een CRUD. Volg de stappen hieronder om dit uit te voeren. In stap 1 maak je een nieuwe database In stap 2-6 maak je een nieuw Yii project aan. De uitgebreide instructie met voorbeelden kan je vinden in les 1. In stap 7-12 maken we de tabellen en de CRUD pagina's. **Voer stap 1 tot en met 13 uit.**
Stap 1, maak een database *coffee.* Stap 2, maak een nieuw Yii project *coffee.* Gebruik de onderstaande regel voor in de Terminal. Let op dat je `` vervangt voor de naam van dit project ``` composer create-project --prefer-dist yiisoft/yii2-app-basic ``` Stap 3, pas de database configuratie file in de Yii aan en laat deze verwijzen naar de *coffee* database. Stap 4, pas de web.php in de Yii config aan en zet smart routing aan (uncomment het gedeelte bij 'urlManager'). Stap 5, maak de file functions.php met daarin de functies dd() en d(). Dit is beschreven in les 1. Stap 6, roep functions.php aan vanuit de web.php configuratie file. Ook dit staat in les 1 beschreven. Stap 7, maak een tabel medewerker. [![image-1616594908052.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616594908052.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616594908052.png) Stap 8, Genereer het model en de CRUD voor *medewerkers* en zet de volgende namen in de database. [![image-1616594707813.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616594707813.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616594707813.png) Stap 9, maak een tabel menu. [![image-1616595011664.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616595011664.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616595011664.png) Stap 10, Genereer het model en de CRUD voor *menu* en zet de volgende namen en prijzen in de database. [![image-1616594739223.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616594739223.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616594739223.png) Stap 11, Maak een tabel bestelling [![image-1616595477669.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616595477669.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616595477669.png) Stap 12, Genereer het model en de CRUD voor *bestelling. (er hoeft geen data in de database gezet te worden voor Bestelling)* We hebben nu een tweede standaard Yii applicatie en we gaan nu aanpassingen maken in het invoerformulier 'Create Bestelling'. Als we nu een nieuwe bestelling maken dan zien we het volgende scherm: [![image-1616595571596.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616595571596.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616595571596.png) Stap 13*, timestamp* halen we weg, want die wordt automatisch ingevoerd (default). Die hoeven we dus niet in te voeren. Open de *\_form.php* in de view van bestelling en haal de *timestamp* regel er uit. (als *id* er ook nog in staat dan kun je deze ook verwijderen uit het formulier) ### Opdracht 2 #### ERD (Database Model) (optioneel alleen als je nog examen KT1 moet doen).
##### Opdracht 2 Maak de *coffee* database met de drie tabellen, menu, medewerker en bestelling een ERD met behulp van Lucichart.
We zijn nu klaar en gaan in de volgende les onze eigen drop down menu's maken. \-- # 8, Form en drop down *We gaan onze koffie applicatie uitbreiden.* *Om het invoeren van gegevens eenvoudiger te maken gaan we drop down menu's toevoegen.* *We gaan naar twee soorten drop-downs kijken; die met vaste waarden en die met dynamische waarden (uit de database).* *Bij het invoeren van een bestelling gaan we allereerst de status via een drop down invoeren.* *Daarna gaan we waarde voor de medewerker ook uit een drop down halen. Deze waarde moet uit de database komen.* ### Drop down in Bestelling We gaan kijken naar de bestelling tabel en CRUD. Als we de status in de database als een enum hebben aangemaakt dan weet Yii dat de status uit beperkt aantal waarden kan hebben en als het goed is maakt Yii dan vanzelf een een drop down in het form. Klopt dat heb jij een drop down? Indien niet, dan kun je de code aanpassen in het form aanpassen. In de view *\_form.php* van Bestellingen moet de volgende regel staan: ```PHP field($model, 'status')->dropDownList([ 'besteld' => 'Besteld', 'klaar' => 'Klaar', 'betaald' => 'Betaald', '' => '', ], ['prompt' => '']) ?> ``` Deze regel laat een drop down menu in het formulier zien. Kijk eens goed naar de parameter van dropDownList. ``` [ 'besteld' => 'Besteld', 'klaar' => 'Klaar', 'betaald' => 'Betaald', '' => '', ], ['prompt' => ''] ``` De eerste parameter is een associative array waarin elk element bestaat uit een key en een value. De key is de waarde die het element uit het form krijgt (de value) en deze waarde wordt door Yii in de database gezet. De key komt dus overeen met de waarde in de database. De value van het associatieve array is wat de gebruiker op het scherm ziet. Deze waarde kan je dus veranderen. Je kunt dus bijvoorbeeld 'betaald' veranderen in 'afgerekend'. Het enige dat dan gebeurt is dat je iets ander op het scherm ziet. In dit voorbeeld is de key en de value gelijk (de waarde op het scherm is hetzelfde als de waarde in de database). Dus samengevat, de key is de waarde in de database en de value is wat de gebruiker op het scherm ziet. Stel we willen in het form van bestelling de medewerker veranderen. Dan moeten we dus de *foreign key* die naar medewerker verwijst veranderen. Het juiste id van de medewerker moet in de tabel bestelling worden ingevuld. De waarde wordt dus het id, maar dat is niet wat je de gebruiker wilt laten zien. Dus als we voor de medewerkers een drop down willen maken dan hebben we een assiociative array nodig dat er zo uit ziet: ``` [ '1'=> 'Ayoub', '2'=> 'Brahim','3'=> 'Carla','4'=> 'Diego','5'=> 'Eisa' ] ``` De keys zijn de id's die als foreign keys in de bestelling tabel staan en de namen zijn de namen van de medewerkers. ### Opdracht 8a Maak nu een statische drop down met de waarden zoals in het voorbeeld (Ayoub, Brahim, Carla, ....). De waarden worden dus (nog) niet uit de database gehaald. ##### Inleveren 1. Schermafdruk yii-08a-jouw-naam met het form waarin je de drop down (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. ### Opdracht 8b, Dynamische drop down In opdracht 8a hebben we een drop down van medewerkers gemaakt, maar we willen de lijst van medewerkers natuurlijk uit de database halen. *In deze opdracht leg ik stap-voor-stap uit wat je moet doen omdat oor elkaar te krijgen. Lees alles aandachtig door en sla geen stappen over!* Data uit de database halen doen we in de controller. Open de BestellingController. Vanuit deze code wordt de create view aangeroepen en vanuit de create view wordt de \_form.php aangeroepen. Waarom dit in twee stappen gaat leggen we later uit. We veranderen de code in Bestelling controller: ```PHP use app\models\Medewerker; .. .. public function actionCreate() { $model = new Bestelling(); $medewerkers = Medewerker::find()->all(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', [ 'model' => $model, 'medewerkers' => $medewerkers ]); } ``` De eerste regel `use app\models\Medewerker;` zetten we bovenaan in de BestellingController. Hiermee vertellen we Yii dat vanuit de *BestellingController* gebruik willen gaan maken van het model *Medewerker*. In de function *actionCreate* die wordt aangeroepen als we een nieuwe bestelling willen maken, voegen we een object toe waarin alle medewerkers zitten. In regel 9 halen we alle medewerkers op en in regel 17 sturen we het resultaat naar de view create.php (van *bestelling*). We kunnen de medewerkers ook ophalen via een sql-query. Je mag regel 9 ook vervangen in de volgende twee regels: ```PHP // dit is hetzelfde als // $medewerkers = Medewerker::find()->all(); $sql="select * from medewerker"; $medewerkers = Yii::$app->db->createCommand($sql)->queryAll(); ``` In de view create.php van bestelling passen we het laatste code aan ```PHP render('_form', [ 'model' => $model, 'medewerkers' => $medewerkers ]) ?> ``` Hiermee geven we het object medewerkers weer door aan de view *\_form* van bestelling. In het view \_form zetten we nu bovenaan in het PHP-gedeelte. ```PHP dd($medewerkers); ``` Dit is de debugfunctie en hiermee controleren we of we inderdaad alle medewerkers naar de view hebben gestuurd. We hebben nu als het goed is een lijst van medewerkers in de \_form maar we moeten het ombouwen naar een assiociative array. Pas hiervoor de code aan in de view \_form van bestelling. ```PHP ... ... ... ``` Deze code laat het eerst stukje van de view\_form zien. Met de functie ArrayHelper::map zetten we het object $medewerkers om in een assiociative array. Met dd() laten we dat zien. Probeer maar! Als het goed is, zie je het volgende: ```PHP [ 1 => 'Ayoub' 2 => 'Brahim' 3 => 'Carla' 4 => 'Diego' 5 => 'Eisa' ] ``` En dit is precies wat we nodig hebben om de Drop Down te maken. Pas de regel in de view \_form van bestelling waarin de user het id van de medewerker moet intypen aan. Verander deze regel in: ```PHP field($model, 'medewerker_id')->dropDownList($medewerkerList, ['prompt' => ''])->label('Medwerker') ?> ``` Als het goed is heb je hiermee een werkend menu gekregen. [![image-1616605065315.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616605065315.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616605065315.png) Gelukt? Wordt de lisjt van medewerkers in de drop down uit de database gehaald? ### Inleveren 1. schermafdruk yii-08b-jouw-naam met het form waarin je de drop down van de medewerkers (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. ### Opdracht 8c
Maak nu een drop down voor de bestelling zodat de medewerker uit een lijstje van koffiesoorten kan kiezen bij het opnemen van de bestelling. [![image-1616605448841.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616605448841.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616605448841.png) Gebruik hiervoor het stappenplan dat hieronder is beschreven.
#### Het stappenplan voor een drop down Schrijf eerst het volgende op: In welk model komt de drop down? Dit noemen we de target. (in opdracht 2 hierboven is dat *Bestelling*) Waar komt de informatie voor de Drop Down uit? Dit noemen we de source. (in opdracht 2 hierboven is dat *Menu*) #### Voer nu de stappen 1-6 uit 1. Zet in de target controller welke source model we willen gebruiken. ``` // in controller van target use app\models\; ``` 2. Haal in de target controller in de actionCreate() de informatie uit de source op. ``` // sourceName bedenk jij zelf en daarin staat de informatie die in de drop down moet komen. $sourceName = source::find()->all(); ``` 3. Geef vanuit de target controller in de actionCreate() de informatie door aan de view. ``` return $this->render('create', [ 'sourceName' => $sourceName, .... ]); ``` 4. Open de view create in de target (view) en geef de informatie door aan \_form view. ``` render('_form', [ 'sourceName' => $sourceName, ... ]); ``` 5. Open de view \_form in de target (view) en zet de informatie om in een assiociative array ``` // in dit voorbeeld worden id en naam gebruikt: // id is het veld dat moet worden opgeslagen // naam is het veld dat in de drop te zien is $sourceNameList = ArrayHelper::map($sourceName,'id','naam'); ``` 6. Pas het invoerveld aan in de \_form van de target (view). ``` // veld_in_de_db is het veld van de target dat in dit invoerveld wordt weggeschreven. field($model, 'veld_in_de_db')->dropDownList($$sourceNameList, ['prompt' => '']) ?> ``` Als je alles correct gedaan hebt dan moet je nog 1 regel aanpassen in de code om de functionaliteit te gebruiken. Yii controleert automatisch of alle databasekolommen zijn ingevuld in het formulier. Dit controleren wordt *validatie* genoemd. Deze regels staan in het model. Omdat je geen Bestelling ID opgeeft bij het maken van een bestelling moet je deze requirement verwijderen op regel 33 van model Bestelling. ```PHP return [ [ ['naam', 'medewerker_id', 'menu_id', 'status'], 'required'], ... ... ] ``` ### Inleveren 1. schermafdruk yii-08c-jouw-naam met het form waarin je de drop down van de producten (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. 2. de *BestellingController.php* en zet jouw naam bij de aanpassingen die je hebt gedaan. ### Opdracht 8d De insert en update lijken op elkaar en gebruiken hetzelfde form (\_form.php). We hebben nu de insert aangepast, maar de update nog niet. Controleer maar! We moeten nu de update dus ook nog aanpassen. #### aanpassen update view Weet je nog dat de -update view niet rechtstreeks door de actionCreate van de BestellingController wordt aangeroepen? Hoe zit dat nu? [![image-1616610519616.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616610519616.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616610519616.png) De *actionCreate* en *actionUpdate vanuit* de controller gaan bieden naar hun 'eigen' view maar via deze eigen view komen beide op hetzelfde form uit. Dat komt omdat een *update* en *create* hetzelfde form gebruiken. Als we nu dus een update uitvoeren van de bestelling (pennetje vanuit de gridview) dan krijgen we een foutmelding. Dat komt omdat de form verwacht dat er gegevens voor de drop down worden meegestuurd. Om dit te fixen kopiëren we de code in de controller die we in de *actionCreate* hebben gemaakt dus naar de *actionUpdate*. Deze twee functies zijn bijna hetzelfde. Bij de create wordt alleen een nieuw object gemaakt en bij de update wordt een bestaand object ingeladen. Nu moeten we de objecten nog doorgeven van de *update* view naar de *\_form* view. Net zoals we dat ook deden bij de *create.*
Zorg ervoor dat de update weer werkt en dat de objecten op de juiste manier worden doorgegeven. Lees hiervoor de uitleg die hierboven staat. Pas de actionUpdate in de BestellingController aan. Pas de update view van de BestellingController aan Zorg dat de update van een Besteliing goed werkt..
### Inleveren 1. de *BestellingController.php* en zet jouw naam bij de aanpassingen die je hebt gedaan. ### ### Opdracht 8e #### De final touch Laten we nog wat labels aanpassen. Dit zijn de teksten die boven de invoervelden in het form staan.
Pas de labels in het form aan zodat er Medewerker, Klantnaam, Bestelling en Status Bestelling komt te staan: [![image-1616611684520.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616611684520.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616611684520.png) Zoek zelf uit hoe dit moet. Tip gebruik de zoektermen: **How to change label text of the ActiveField?**
### Inleveren 1. schermafdruk yii-08e-jouw-naam met het bestellingen form waarin je de aangepaste labels laat zien. Maak een schermafdruk van je gehele browser. \-- # 9, Drop down in de Grid View *In deze les gaan we bepaalde dingen herhalen en op een ander manier gebruiken. We leren hoe we drop downs in een Gridview plaatsen en we leren ook hoe we een relatie kunnen gebruiken in de gridview.* In de gridview van de *Bestellingen* pas de kolom **status** aan: ```PHP [ 'attribute'=>'status', 'filter'=>array('besteld'=>'is besteld', 'klaar'=>'is klaar', 'betaald'=>'is betaald'), ], ``` In de database is de waarde van deze kolom *besteld*, *klaar* of *betaald*. Om duidelijk het verschil tussen de waarde van de kolom in de database en de getoonde waarde te laten zien zijn de getoonde waardes 'is besteld', 'is klaar' en 'ís betaald'. Let op dat ``` array('besteld'=>'is besteld', 'klaar'=>'is klaar', 'betaald'=>'is betaald') ``` hetzelfde is als ``` ['besteld'=>'is besteld', 'klaar'=>'is klaar', 'betaald'=>'is betaald'] ``` Als we nu een drop down willen bij de medewerkers dan moeten we dit hetzelfde aanpakken als we in de vorige les hebben gedaan: Bovenaan in de view zetten we: ```PHP $medewerkerList=['1'=>'test1','2'=>'test2','3'=>'test3']; ``` Test dit uit. Als je test1 selecteert dan selecteer je dus de medewerkers met id 1. Vanuit de controller maken we een object dat alle medewerkers bevat. Dit object geven we door aan de grid view (index). Daar maken we van het object een list en de list plaatsen we in de kolom als value van de key 'filter'. Dus we veranderen de kolom medewerker: ```PHP [ 'attribute' => 'medewerker_id', 'filter' => $medewerkerList, ], ```
##### Opdracht 1 Zorg er nu voor dat je vanuit de controller de medewerkers opvraagt en deze verstuurd naar de index view. Gebruik de `ArrayHelper::map()` functie om je object om te zetten in een list. En gebruik de list dan in de gridview.
In de kolom medewerker zien we nu de id's van de medewerker. Als we de naam willen afdrukken moeten we de relatie maken. Dit hebben we eerder gedaan, zet in het model van Bestelling de volgende code: ```PHP public function getMedewerkers() { return $this->hasOne(Medewerker::className(), ['id' => 'medewerker_id']); } ``` Plaats nu in de gridview in de index view van de bestelling: ```PHP [ 'attribute' => 'medewerker_id', 'label' => 'Medewerker', 'filter' => $medewerkerList, 'value' => 'medewerkers.naam' ], ``` Hiermee zetten we de waarde van deze kolom op *naam* van de relatie *medewerkers*.
##### Opdracht 2 Pas tenslotte het label aan en zorg ervoor dat de kolom waarin de medewerker wordt getoond in de gridview (van de index view van bestelling) er als volgt uit ziet: [![image-1616622153704.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616622153704.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616622153704.png) Als er een naam wordt geselecteerd dan worden alle bestellingen die door deze medewerker zijn uitgevoerd geselecteerd.
Hiermee zetten we de waarde van deze kolom op *naam* van de relatie *medewerkers*.
##### Opdracht 3 In deze opdracht gaan we alle stappen die we hebben uitgevoerd om de kolom *Medewerker* aan te passen nog een keer uitvoeren maar nu voor de kolom *menu\_id*. Pas de *menu\_id* kolom aan in het bestellingenoverzicht (*index* view van *Bestelling*). Zorg ervoor dat je met een drop down alle koffie soorten (*menu*) kunt selecteren. Je kunt dan dus alle bestellingen 'Americano' opzoeken. [![image-1616622602808.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616622602808.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616622602808.png) Zorg ervoor dat je de juiste relatie legt tussen de *Bestelling* en *Menu*. Pas dan de grid view aan zodat je in de bestelling kolom geen id's meer ziet, maar dat je de naam van de bestellingen ziet. [![image-1616622580235.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616622580235.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616622580235.png) Let ook op het label. Dit is veranderd van *menu\_id* naar *Bestelling*. **Succes!**
\-- # 10. View en details aanpassen Om alle details netjes te krijgen dien je nog de view.php aan te passen zodat de labels correct staan.
##### Opdracht 1 Pas de gegevens van de tabel in de **view.php** zodanig aan dat het resultaat er ongeveer zo uit ziet als onderstaande afbeelding: Let op! Er is een relatie tussen Bestelling en Medewerker. Wil je van de properties van medewerker gebruik maken doe dan dit: $model->menu->naam **Tip: er is ook een relatie tussen Bestelling en Menu**
[![Screenshot-2021-05-29-140727.png](http://roc.ovh/uploads/images/gallery/2021-05/scaled-1680-/Screenshot-2021-05-29-140727.png)](http://roc.ovh/uploads/images/gallery/2021-05/Screenshot-2021-05-29-140727.png)
#### Opdracht 2 Boven elke pagina kun je **breadcrumbs** zien die aangeven waar een gebruiker zich bevindt. Als je de opdrachten gemaakt hebt volgens de instructies dan zie je dat er *Bestellings* staat. Dit is natuurlijk niet correct. Kun je dit aanpassen naar *Bestellingen.* **Tip: dit dien je te doen op meerdere views.**
# 11. Login *Yii heeft al een (beperkte) ingebouwde login. In deze les wordt besproken hoe je dit systeem kan gebruiken.* ### Role based security In *role based security* heeft elke gebruiker een naam en wachtwoord en heeft elke gebruiker ook een rol (role in het Engels). De role bepaald dan wat de gebruiker kan. Je kunt de rol 'admin' hebben die bijvoorbeeld alles kan en mag. Daarnaast kun je de rol 'user' hebben die bijvoorbeeld alleen bepaalde overzichten kan maken. Een andere rol zou bijvoorbeeld 'docent' kunnen zijn en de docent kan dan bijvoorbeeld cijfers aanpassen. De rol leerling kan dan bijvoorbeeld alleen de cijfers raadplegen. ### Model De file in model 'User.php' bevat de data waarin de users staan. De users staan dus niet in de database. Dat kan wel maar dat kost iets meer tijd en voor het ontwikkelen is het lekker 'makkelijk' om de gebruikers even lokaal aan te maken. Standaard staat dit in de *User.php* file ```PHP private static $users = [ '100' => [ 'id' => '100', 'username' => 'admin', 'password' => 'admin', 'authKey' => 'test100key', 'accessToken' => '100-token', 'role' => 'admin', ], '101' => [ 'id' => '101', 'username' => 'demo', 'password' => 'demo', 'authKey' => 'test101key', 'accessToken' => '101-token', 'role' => 'user' ], ]; ``` Regel 8 en 16 (de 'role') zijn toegevoegd aan de standaard inhoud van de file. In dit voorbeeld zijn er dus twee rollen. Dan dien je nog een attribuut/property toe te voegen aan de User Model. Door deze als volgt aan te maken: Voeg `public $role` toe bovenaan in de class User ```PHP class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface { public $id; public $username; public $password; public $authKey; public $accessToken; public $role; //Deze dien je nog toe te voegen aan de User.php ``` Dit is alles en hiermee kan kan je aanloggen in het Yii systeem. ### Functionaliteiten afschermen In elke controller staat bovenin een function behaviors(). Deze functie regelt de toegang tot de functies. Bijvoorbeeld: ```PHP public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // when logged in, any user but yo need to be logged in! [ 'actions' => ['student','create', 'update-status'], 'allow' => true, 'roles' => ['@'], ], // when logged in and role = user [ 'actions' => [ 'rolspeler', 'update', 'call-student' ], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'user'); } ], // when logged in and role = admin [ 'actions' => [], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'admin'); } ], ], ], ]; } ``` ##### Toelichting Het eerste gedeelte is standaard en daarin wordt bepaald dat een delete alleen mag worden aangeroepen via een POST (en dus niet via een GET). Dit is niet van belang voor onze role based security. ```PHP 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], ``` Het gedeelte daaronder is wel van belang. We zien drie blokjes (elk blokje begint met 'actions'). In het eerste blokje wordt bepaald dat als je bent aangelogd dat je dan de functie *student*, *create* en *update-status* mag gebruiken. ```PHP // when logged in, any user but yo need to be logged in! [ 'actions' => ['student','create', 'update-status'], 'allow' => true, 'roles' => ['@'], ], ``` De regel` 'roles'=> ['@']` betekent iedereen die is ingelogd. Je kunt ook `'roles'=>['?']` gebruiken en dat betekent iedereen die niet is aangelogd (*guest* user). Meer uitleg kan je vinden op: [https://www.yiiframework.com/doc/guide/2.0/en/security-authorization](https://www.yiiframework.com/doc/guide/2.0/en/security-authorization) Het tweede blokje bepaald dat als je bent aangelogd met de rol *user*, je de functie *rolspeler*, *update* en *call-student* mag gebruiken. ```PHP // when logged in and role = user [ 'actions' => [ 'rolspeler', 'update', 'call-student' ], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'user'); } ], ``` Het derde blokje bepaald dat je alle acties (dus alle functies) kan gebruiken als he de rol *admin* hebt. ```PHP // when logged in and role = admin [ 'actions' => [], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'admin'); } ], ``` ### Opgave 1 Maak allereerst een menu voor jouw *coffee* app. [![image-1621708382447.png](https://www.roc.ovh/uploads/images/gallery/2021-05/scaled-1680-/image-1621708382447.png)](https://www.roc.ovh/uploads/images/gallery/2021-05/image-1621708382447.png) Kijk in[ les 3](https://www.roc.ovh/books/yii/page/3-menu "Yii - Les 3, Menu") als je niet meer weet hoe je een menu moet maken. ### Opgave 2 Maak nu een drie logins: één admin en twee medewerkers Adam en jane. Gebruik de volgende gegevens.
idusernamepasswordauthKeyaccesTokenrole
100adminadmin123admin-keyadmin-tokenadmin
200Adamgeheimadam-keyadam-tokenuser
201Janewachtwoordjane-keyjane-tokenuser
Pas de *User.php* in *models* aan. Kijk naar het voorbeeld dat in deze les is beschreven. Je kunt de template die hieronder staat gebruiken. ```PHP private static $users = [ '100' => [ 'id' => '100', 'username' => '', 'password' => '', 'authKey' => '', 'accessToken' => '', 'role' => '', ], '200' => [ 'id' => '200', 'username' => '', 'password' => '', 'authKey' => '', 'accessToken' => '', 'role' => '' ], '201' => [ 'id' => '201', 'username' => '', 'password' => '', 'authKey' => '', 'accessToken' => '', 'role' => '' ], ]; ``` ### Opgave 3 Wie mag wat? Pas de rechten aan aan de hand van het volgende schema.
CreateRead (index)UpdateDelete
Medewerkersadminmedewerker/admin2)adminadmin
Menusadminiedereen1)adminadmin
Bestellingmedewerkeriedereen1)medewerker/admin2)medewerker/admin2)
1)*Iedereen* betekent admin, medwerker of iemand die *niet* is aangelogd. 2)*medewerker/admin*, kan ook worden gezien als iemand die is aangelogd. Bovenstaande tabel kun je ook zo vertalen: Een **admin** kan
MedewerkerRead (index)CreateUpdateDelete
MenusRead (index)CreateUpdateDelete
BestellingRead (index)CreateUpdateDelete
Een **medewerker/user** kan
MedewerkerRead (index)
MenusRead (index)
BestellingRead (index)CreateUpdateDelete
Een **niet-ingelogde gebruiker/guest** kan
Medewerker
MenusRead (index)
BestellingRead (index)
We gaan de toegang regelen voor de *create*, *index*, *update* en *delete* van de *medewerker*. Ga naar de file *MedewerkerController.php* in de *controllers* en voeg de volgende code toe: ```PHP class MedewerkerController extends Controller { public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // when logged in as admin [ 'actions' => ['create', 'update', 'delete'], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'admin' ); } ], // when logged in, any user but you need to be logged in! [ 'actions' => ['index'], 'allow' => true, 'roles' => ['@'], ], ], ], ]; } .... .... ``` Maak nu zelf de toegangscontrole voor de *MenuController.php* en de *BestellingController* af. ### More info In [https://www.roc.ovh/books/yii/page/login-via-db](https://www.roc.ovh/books/yii/page/login-via-db) staat kort beschreven hoe je de gebruikers in de database kan vastleggen. Voor het examen is dit niet noodzakelijk. \-- # 12. Issues Deze opdracht maakt gebruik van een bestaand project waarbij sommige zaken ontbreken of kapot zijn. Aan jou de taak om ze op te lossen Maak gebruik van het project op Github: [Garage](https://github.com/MeesterDaaf/yii-garage-issues) 1. Maak een Fork van de repository [![Screenshot-2021-06-15-165045.png](http://roc.ovh/uploads/images/gallery/2021-06/scaled-1680-/Screenshot-2021-06-15-165045.png)](http://roc.ovh/uploads/images/gallery/2021-06/Screenshot-2021-06-15-165045.png) 1. Clone (vanuit je eigen Github) het project naar je eigen computer 2. In de root folder van het project staat een bestand: `garage.sql` 3. Maak een database aan: `garage` 4. Importeer het garage.sql met phpmyadmin`` 5. Open het project in VS Code 6. Run de volgende code: `composer update` (hiermee update je de benodigde pakketen voor dit project) 7. Start de server op met `php yii serve``` 8. `Los de onderstaande issue op`: zorg ervoor dat je na elke oplossing een `commit maakt` 9. Als je klaar bent `push` je je werk naar Github`` ##### Issues 1. Part pagina is niet zichtbaar 2. Bij maken Car is het mogelijk om klanten te selecteren maar we zien alleen voornamen, achternamen zou al veel beter zijn 3. In de appointment index staat onder Customer een getal. Hier moet een achternaam komen. 4. Bij maken van een reparatie worden de onderdelen (Part) niet goed weergegeven in de dropdown. 5. Als je een Appointment, Car of Reparatie wilt updaten dan ontstaat er een error. 6. Inloggen doet het niet vanwege een bug. 7. Alle pagina's zijn zichtbaar zonder inloggen: klant wil dit 8.
Index Create Update Delete
Appointment Alle medewerkers Alle medewerkers Admin/Manager Admin
Car Alle medewerkers Alle medewerkers Admin/Manager Admin
Repair Alle medewerkers Alle medewerkers Admin/Manager Admin
Customer Alle medewerkers Alle medewerkers Admin/Manager Admin
Parts Iedereen Alle medewerkers Admin/Manager Admin
# 13. PDF genereren (optioneel) Je kunt data exporteren naar bijvoorbeeld een excel bestand of PDF. Dit kan ook met YII maar daarvoor heb je wel een extentie nodig die dit voor je regelt. Hoe dat in zijn werk gaat dat zie je in dit hoofdstuk. 1\. Installeer de volgende extentie: **yii2-export** door de volgende command in de terminal van je project te zetten: `composer require kartik-v/yii2-export "*"` 2\. Zorg ervoor dat de extentie in je project geactiveerd is. Open `web.php` Voeg de onderstaande code toe achter 'components' ```PHP 'components' => [ ], 'modules' => [ 'gridview' => ['class' => 'kartik\grid\Module'] ], ``` 3\. Kies een Controller uit waar je de PDF-extentie wilt gebruiken. Stel bijvoorbeeld dat je een overzicht wilt printen van alle Projecten of alle Auto's. Je kunt in principe van alle *index* overzichten een pdf (of excel) maken. In het voorbeeld hieronder maken we gebruik van een *AppointmentController* Bovenin de controller zet je de volgende regel neer `use kartik\export\ExportMenu;` 4\. We verbouwen de boel een beetje zodat de logica klopt. In de view index.php wordt de gridview aangemaakt. Dit gaan we aanpassen Van dit:
```PHP


        'dataProvider' => $dataProvider,

        'filterModel' => $searchModel,

        'columns' => [

            ['class' => 'yii\grid\SerialColumn'],





            'id',

            [

                'attribute' => 'appointment_id',

                'label'     => 'Appointment',

                'value'     => 'appointment.name'

            ],

            [

                'attribute' => 'part_id',

                'label'     => 'Part',

                'value'     => 'part.name'

            ],





            ['class' => 'yii\grid\ActionColumn'],

        ],

    ]); ```
Naar dit: Hiermee worden de kolommen van de tabel niet aangemaakt in de view index.php maar in de controller zelf. ```PHP $dataProvider, 'filterModel' => $searchModel, 'columns' => $gridColumns, ], ); // Renders a export dropdown menu echo $menu; ?> ``` 5\. Omdat we een overzicht willen hebben van alle Afspraken, dus de index, gaan we naar de method *actionIndex()* van de desbetreffende controller (in dit voorbeeld de AppointmentController. ```PHP // AppointmentController.php $gridColumns = [ ['class' => 'yii\grid\SerialColumn'], 'id', [ 'attribute' => 'appointment_id', 'label' => 'Appointment', 'value' => 'appointment.name' ], [ 'attribute' => 'part_id', 'label' => 'Part', 'value' => 'part.name' ], ['class' => 'yii\grid\ActionColumn'], ]; $menu = ExportMenu::widget([ 'dataProvider' => $dataProvider, 'columns' => $gridColumns ]); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, 'gridColumns' => $gridColumns, 'menu' => $menu, ]); ``` # FAQ #### Hoe maak ik een link met Yii? ```PHP use yii\helpers\Html; ... Html::a('Naam van de link', ['/controller/method','id'=>'1','param2'=>'waarde'],['title'=> 'Edit',]); ``` Dit resulteert in de link met de naam 'Naam van de link' die verwijst naar link /controller/method?id=1&param2=waarde Verder die je de tekst 'Edit' als je een met de muis over de link gaat (mouse over). #### Hoe maak ik een button in Yii? ```PHP use yii\helpers\Html; ... 'btn btn-primary', 'title'=> 'Mouse over Text',]) ?> 'btn btn-success', 'title'=> 'Mouse over Text',]) ?> ``` Bootstrap button styles: [https://getbootstrap.com/docs/4.0/components/buttons/](https://getbootstrap.com/docs/4.0/components/buttons/) #### Hoe verander ik de standaard home page in Yii? Twee mogelijkheden: 1. Standaard staat de home page in de site view index, views/site/index.php. Pas deze file aan. 2. De controller SiteController.php wordt standaard uitgevoerd. Je kan hier dus een verwijzing naar andere home pagina maken. Zie voorbeeld hieronder. ```PHP public function actionIndex() { return $this->redirect(['/maaltijd/index']); } ``` De home page is nu /maaltijd/index. #### Hoe kan ik vanuit Yii naar de vorige pagina? ```PHP use yii\helpers\Html; ... request->referrer , ['class'=>'btn btn-primary']); ?> ``` #### Hoe kan ik selecteren en sorteren? ```PHP $countries = Country::find()->where(['Continent' => 'Europe']) ->andWhere('SurfaceArea>1000000')->orderBy(['Name' => SORT_ASC])->all(); ``` Hierbij selecteer je alle countries waarbij Continent = 'Europe' and waar SurfaceArea>1000000, gesorteerd op Name (oplopend, dus a...z). #### Hoe kan ik in Yii invoercontrole instellen? Dit bepaal je in je model, bijvoorbeeld: ```PHP public function rules() { return [ [['population', 'average_age'], 'integer'], [['children_count'], 'integer', 'min' => 0, 'max' => 5], [['first_name', 'last_name'], 'string', 'max' => 35], [['from_date', 'to_date'], 'date'], [['opmerking', 'extra_opmerking'], 'default', 'value' => null], [['email'], 'email'], ]; } ``` Meer info: [https://www.yiiframework.com/doc/guide/2.0/en/tutorial-core-validators](https://www.yiiframework.com/doc/guide/2.0/en/tutorial-core-validators) en [https://www.yiiframework.com/doc/guide/2.0/en/input-validation](https://www.yiiframework.com/doc/guide/2.0/en/input-validation) # Trouble Shooting Yii Installatie Eerste hulp bij installatie issues. Yii is eigenlijk alleen afhankelijk van PHP. Als Yii dus niet werkt dan moet er worden gecontroleerd of de goede versie van XAMPP is geïnstalleerd en of deze versie ook daadwerkelijk wordt gebruikt. #### Stap 1, nieuwst XAMPP Controleer XAMPP, alles is getest met de onderstaande versie, zorg dat je deze versie of nieuwer heb geïnstalleerd en dat oude versie zijn verwijderd. [![image-1600615612510.png](https://www.roc.ovh/uploads/images/gallery/2020-09/scaled-1680-/image-1600615612510.png)](https://www.roc.ovh/uploads/images/gallery/2020-09/image-1600615612510.png) #### Stap2, welke php wordt gebruikt? Om dit te controleren maken we een nieuwe file *test.php* en zetten deze in de web directory van het Yii project. In opgave 1 van de lessen, heet het project *world* dus we plaatsen de file *test.php* in de directory *world/web*. Meestal is het path dus voluit *c:\\XAMPP\\htdocs\\world\\web,* maar dit kan per installatie verschillen. In de file *test.php* komt het volgende te staan: ``` ``` Start nu je yii server op (*php yii serve*). In de browser ga je naar [http://localhost:8080/test.php](http://localhost:8080/test.php) Je ziet nu alle instellingen van de PHP server. [![image-1600616185913.png](https://www.roc.ovh/uploads/images/gallery/2020-09/scaled-1680-/image-1600616185913.png)](https://www.roc.ovh/uploads/images/gallery/2020-09/image-1600616185913.png) De geel gemarkeerde dingen zijn belangrijk. De eerste laat zien dat PDO is geïnstalleerd: ``` "--with-pdo-oci=c:\php-snap-build\deps_aux\oracle\x64\instantclient_12_1\sdk,shared" ``` De tweede laat zien welke PHP wordt gebruikt. ``` Loaded Configuration File C:\xampp\php\php.ini ``` Ziet dit er anders uit dan heb je mogelijk een andere PHP versie nog geïnstalleerd of die heb je geïnstalleerd gehad. Haal deze weg of de-installeer deze. #### Stap 3, path Ga naat je *cmd box* en type *path*. Zoek in het de output van dit commando naar: ``` C:\xampp\php ``` Lastig zoeken? Kopieer de hele output naar *notepad* en gebruik de zoekfunctie van *notepad* en zoek naar *php*. Contoleer ook of er niet twee maal een path naar php bestaat (php komt als het goed is precies één keer voor). Klopt het path niet dan kan je dat als volgt aanpassen. 1. ga naar *control panel* 2. zoek (rechtsbovenin) naar *environment* [![image-1600616724024.png](https://www.roc.ovh/uploads/images/gallery/2020-09/scaled-1680-/image-1600616724024.png)](https://www.roc.ovh/uploads/images/gallery/2020-09/image-1600616724024.png) 3. Klik op "Edit the system environment variables" 4. Klik op knop "Environment Variables" [![image-1600617058469.png](https://www.roc.ovh/uploads/images/gallery/2020-09/scaled-1680-/image-1600617058469.png)](https://www.roc.ovh/uploads/images/gallery/2020-09/image-1600617058469.png) 5. Je hebt nu twee schermen (boven en onder). Zoek waar je *path* staat en pas het aan. 6. Had je geen *path*? Maak dan een nieuwe entry in de system variables (onderste gedeelte van het scherm). Je moet nu herstarten of uitloggen/opnieuw aanloggen. Controleer dan opnieuw in een cmd box met het commande *path* het *path* goed staat. \-- ### XAMPP opstarten of afsluiten geeft foutmelding Bij het afsluiten van XAMPP krijg je een foutmelding die zegt dat het bestand xampp-control.ini niet kan worden overschreven. Je kunt dit verhelpen door de inhoud van deze file eerst te kopiëren, dan de file weg te gooien en daarna een nieuwe xampp-control.ini aan te maken. Zet daarin de originele inhoud, die je hebt gekopieerd terug. ### De CRUD en Model page werkt niet Je gaar naar [http://localhost:8080/gii](http://localhost:8080/gii) en er gebeurt niets. Heb je de *urlManager* in web.php goed geconfigureerd (zie uitleg in les1)? ### Gii werkt niet Als je gii oproept dan zie je de Yii home page. Check config/web.php is op regel 47 is dit stukje uncommented? \-- I put this in the page content for our homepage rather than in the custom HTML header where you'd only want: # Bijlagen, Notes en Voorbeelden Voorbeelden stukjes code # Unsorted Notes ### Voor- nadelen Laravel v. Yii
LaravelYII
MVC
ModelEloquentActive Record
View/templatingBladePHP met 'Yii Snippets'
RoutingExplicietImpliciet / Expliciet
Migrations (meer als optie)
Model creationscommand line GUI, reverse engineered form DB
Controller Creationcommand line GUI, create full CRUD
View creation? GUI, create full CRUD
Documentation\*\*\* \*\*
Active development
Add Ons / Libraries\*\*\*\*\*
Install Base (volgens Google)484,97058,800
Grootste voordeel is de GUI waarmee je reverse engineered een CRUD maakt. ### Install Datepicker (in project root) change in file composer.dev "minimum-stability": "dev", ``` composer clear-cache composer require kartik-v/yii2-widget-datepicker ``` [https://github.com/kartik-v/yii2-widget-datepicker](https://github.com/kartik-v/yii2-widget-datepicker) ##### Create (temporary swap on Ubuntu) ``` free -m total used free shared buffers cached Mem: 2048 357 1690 0 0 237 -/+ buffers/cache: 119 1928 Swap: 0 0 0 ``` ``` /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 /sbin/mkswap /var/swap.1 /sbin/swapon /var/swap.1 ``` [https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors](https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors) ### Install Bootstrap 4 ``` composer require --prefer-dist yiisoft/yii2-bootstrap4 ``` ### Redirect ```PHP return $this->redirect(['view', 'id' => $model->id]); ``` ### Post form on dropdown change ```PHP ``` *gesprek* in dit voorbeeld de model naam, en *status* is de naam van het form-veld. Dit veld wordt in de standaard update automatisch gesaved. ### Drop down (look-up in forms) In controller get data for dop down, stel het model/table heet xxx. ```PHP use app\models\xxx; ... $xxxModel = xxx::find()->all(); ... return $this->render('create', [ 'model' => $model,   'xxxModel' => $xxxModel, ]); ``` ##### Dan in form ```PHP use yii\helpers\ArrayHelper; ... field($model, 'foreignKey')->dropDownList($itemList,['prompt'=>'Please select']); ?> ``` ##### Change label ``` field($model, 'databasecolumn')->textInput()->label('Your nice label') ?> ``` # Helpers ##### HTML::a ```PHP // both the same return Html::a($data->naam, ['/examen/update?id='.$data->id],['title'=> 'Edit',]); return Html::a($data->naam, ['/examen/update', 'id'=>$data->id], ['title'=> 'Edit',]); // back request->referrer , ['class'=>'btn btn-primary']); ?> ``` ##### Array helper ```PHP use yii\helpers\ArrayHelper; $dropdownlist = Arrayhelper::map($student,'id','naam'); //input [ [ 'id' => '999', 'naam' => 'Mark de Groot'], [ 'id' => ..... ], [ ....], ....] //result [ '999' => 'Mark de Groot', ...]; ``` ##### Link met confirm (gidview Widget) ```PHP echo Html::a("", ['print/index', 'id'=>-99], [ 'title' => 'Print ALL', 'data' => [ 'confirm' => 'Are you sure you want to print this item?', 'method' => 'post', ], ] ); ``` #### Link met confirm en tooltip (Html::a) ```HTML Html::a("", ['/uitslag/remove', 'studentid'=>$dataSet[$naam]['studentid'], 'examenid'=>$examenid ], ['data-confirm' => 'Delete all results from '.$naam.' from this exam?', 'title'=> 'Verwijder uitslag']); ``` Html::a( \[naam\], \[link, parameter, parameter\], \[options\] ) Options in het bovenstaande voorbeeld: *data-confirm* en *title*. Andere option is *class* #### Yii::$app ```PHP Yii::$app->requestedRoute; // route, example query/help Yii::$app->controller->action->id // action, example help ``` # Bootstrap 4 ### Install ``` composer require --prefer-dist yiisoft/yii2-bootstrap4 ``` In composer.json ``` ... "minimum-stability": "dev", "require": { ... "yiisoft/yii2-bootstrap4": "^1.0@dev" ... ``` in site.css (optional) ``` // Added by MB .nav { font-size: 2.4em; } .nav li { padding-right:0px; font-size: 1.4em; } ul.nav a:hover { color:#a94442; background-color: #E0E0E0; } h1 { color:#424ca9; font-style: italic; } ``` # Views ### Include HTML ```p1 view->renderFile('@app/views/site/fileToInclude.php'); ?> ``` ### Set Flash Terugkoppeling vanuit de controller naar de view. #### In Controller ```PHP Yii::$app->session->setFlash('success', "Exam deleted."); of Yii::$app->session->setFlash('error', "Exam cannot be deleten, it has still forms attached to it."); ``` #### In View ``` session->hasFlash('success')): ?>
session->getFlash('success') ?>
session->hasFlash('error')): ?>
session->getFlash('error') ?>
``` ### Redirect ```PHP return $this->redirect(['index']); ``` # Two database connections Zie; http://www.bsourcecode.com/yiiframework2/yii2-0-database-connection/ # Nav - Menu's ##### Example menu. Menu 1 contain a confirmation dialog, menu 2 contains an external URL/Link. ```PHP false, 'items' => [ [ 'label' => 'Menu1', 'visible' => (isset(Yii::$app->user->identity->role) && Yii::$app->user->identity->role == 'admin'), 'items' => [ ['label' => 'Item1', 'url' => ['/control/action'] ], ['label' => 'Item2', 'url' => ['/control/action'] ], ['label' => 'Item3', 'url' => ['/control/action'] ], [ 'label' => 'Item4', 'url' => ['/control/action'], 'linkOptions' => array('onclick'=>'return confirm("Are you sure? ")'), ], ['label' => '-----------------------------------'], ['label' => 'Item1', 'url' => ['/control/action'] ], ], 'options' => ['class' => 'nav-item'] ], [ 'label' => 'Menu2', 'visible' => (isset(Yii::$app->user->identity->role) && Yii::$app->user->identity->role == 'admin'), 'items' => [ ['label' => 'Item1', 'url' => ['/control/action'] ], ['label' => 'Item2', 'url' => ['/control/action'] ], [ 'label' => 'Item3', 'url' => 'https://server.com/page1', 'template'=> '{label}', 'linkOptions' => ['target' => '_blank'], ], ], 'options' => ['class' => 'nav-item'] ], ], ]); ``` ##### Split menu from main.php ```PHP // In main.php // remove the use *navbar* lines (2)
// remove menu here and replace it with this line view->renderFile('@app/views/layouts/menu.php'); ?>
// now you can put your menu in the file menu.php (same directory) ``` ```PHP // menu.php Yii::$app->name, 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar navbar-expand-md navbar-dark bg-dark fixed-top', ], ]); echo Nav::widget([ 'options' => ['class' => 'navbar-nav'], 'items' => [ ... ... ``` # Joined Table Search Case *Gesprek* has an N:1 relation with *Student*. *Gesprek* has a FK *studentid* The table student has a *naam* #### Gesprek Model Create relation In het Model Gesprek van het object ``` public function getStudent() { return $this->hasOne(Student::className(), ['id' => 'studentid']); } ``` #### Gesprek View In de GridView Widget ``` [ 'attribute' => 'student', 'value' => 'student.naam', ], ``` #### GesprekSearch model 1. line 3 add the public variable 2. line 9 add student searchbox 3. line 19 add join 4. line 22-24 add sorting 5. line 27 add filter in query ```PHP class GesprekSearch extends Gesprek { public $student; // ADD student! public function rules() { return [ [['formid', 'rolspelerid', 'status'], 'integer'], [['opmerking', 'student'], 'safe'], // ADD student! ]; } ... public function search($params) { $query = Gesprek::find() ->joinwith(['examen', 'form', 'student']) // ADD student! ... $dataProvider->sort->attributes['student'] = [ // ADD this block to suppoer sorting 'asc' => ['student.naam' => SORT_ASC], 'desc' => ['student.naam' => SORT_DESC], ]; ... $query->andFilterWhere(['like', 'student.naam', $this->student]); // ADD filter! ``` See: [https://www.yiiframework.com/wiki/653/displaying-sorting-and-filtering-model-relations-on-a-gridview](https://www.yiiframework.com/wiki/653/displaying-sorting-and-filtering-model-relations-on-a-gridview) # Resources #### Official docs
[https://www.yiiframework.com/doc/api/2.0](https://www.yiiframework.com/doc/api/2.0)
#### Yii2 Quick Start Guide [http://quickstart.yii2tutorial.com/content/ch11#11-1](http://quickstart.yii2tutorial.com/content/ch11#11-1) #### Yii2 Lessons
[https://www.youtube.com/playlist?list=PLRd0zhQj3CBmusDbBzFgg3H20VxLx2mkF](https://www.youtube.com/playlist?list=PLRd0zhQj3CBmusDbBzFgg3H20VxLx2mkF "https://www.youtube.com/playlist?list=plrd0zhqj3cbmusdbbzfgg3h20vxlx2mkf")
#### Yii2 Tutorials
[https://www.youtube.com/playlist?list=PLK757tmJ34tNGMy5uzD-Gn\_wpjavHFbon](https://www.youtube.com/playlist?list=PLK757tmJ34tNGMy5uzD-Gn_wpjavHFbon)
#### Beginning Yii2
[https://www.youtube.com/playlist?list=PLMyGpiUTm106xkNQh9WeMsa-LXjanaLUm](https://www.youtube.com/playlist?list=PLMyGpiUTm106xkNQh9WeMsa-LXjanaLUm)
#### Building a Youtube Clone
[https://www.youtube.com/watch?v=whuIf33v2Ug](https://www.youtube.com/watch?v=whuIf33v2Ug)
#### Russian Forum (translated in English)
[https://translate.google.com/translate?hl=en&sl=ru&tl=en&u=https%3A%2F%2Fyiiframework.ru%2Fforum%2F\\](https://translate.google.com/translate?hl=en&sl=ru&tl=en&u=https%3A%2F%2Fyiiframework.ru%2Fforum%2F)
#### Examples (web sites build in Yii) 1. [https://yiipowered.com/en](https://yiipowered.com/en) 2. [https://monoray.ru/products/6-open-real-estate](https://monoray.ru/products/6-open-real-estate) 3. [https://sakuracommerce.com/](https://sakuracommerce.com/)
# Login - Copied
#### Customize Authentication On Yii Framework Using MySQL Database
Just managed to find some time to play around with Yii. Yii is powerful but there is still a long way to go if we are talking about documentation for Yii framework. In Yii framework, we can see that it is very different from CodeIgniter where documentation is really structured and well understood. Nonetheless, i still feel that Yii framework is worth to explore. I managed to get my own customized authentication on Yii by adding some secure feature such as hashing, salt, key and etc. So here i am writing this tutorial to share with you more about Yii framework using MySQL database. ## Requirement Since this is more like a follow up tutorial, there are a few requirements before you start reading this tutorial.
1. [Installed Yii with a MySQL database](http://hungred.com/how-to/image-tutorial-setup-yii-framework-wamp-mysql-database/). 2. [Setup Gii and get the user CRUD completed](http://www.yiiframework.com/doc/guide/quickstart.first-app).
## Customize Authentication - Database Now here comes the tricky part. We need a database that stored our hashed password which is 128 bits since i am using sha512. Our data schema should looks like this,
```SQL CREATE TABLE tbl_user (     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,     username VARCHAR(128) NOT NULL,     password VARCHAR(128) NOT NULL,     email VARCHAR(128) NOT NULL ); ``` Well, it looks the same as the demo one so just ignore me lol. Create this table and we are ready to do some MVC. If you are following the tutorial you would most likely get a CRUD user setup. But we only have 'admin' and 'demo' login account. We would definitely want something better. ## Customize Authentication - Model In order to validate a user, we need to create a few methods in the model folder in order to authenticate and store user password. We will need to create these functions on our User.php file on our model folder. ```PHP /**  * @return boolean validate user  */ public function validatePassword($password, $username){     return $this->hashPassword($password, $username) === $this->password; } /**  * @return hashed value  */ DEFINE('SALT_LENGTH', 10); public function hashPassword($phrase, $salt = null){     $key = 'Gf;B&yXL|beJUf-K*PPiU{wf|@9K9j5?d+YW}?VAZOS%e2c -:11ii<}ZM?PO!96';     if($salt == '')         $salt = substr(hash('sha512', $key), 0, SALT_LENGTH);     else         $salt = substr($salt, 0, SALT_LENGTH);     return hash('sha512', $salt . $key . $phrase); } ``` the two methods above is used to validate the user password during login and the other method returns a hashed password given the original plain password value. Once these two methods are pasted into the user.php file. We are done with our modal! ## Customize Authentication - Controller In controller, we need to modify the create and update handler but i will just demonstrate the create user handler. Go to your controller folder and look for UserController.php. Change the method actionCreate to the following
```PHP /**  * Creates a new model.  * If creation is successful, the browser will be redirected to the 'view' page.  */ public function actionCreate() {     $model=new User;       // Uncomment the following line if AJAX validation is needed     // $this->performAjaxValidation($model);       if(isset($_POST['User']))     {         $model->attributes=$_POST['User'];         $model->password = $model->hashPassword($_POST['User']['password'], $_POST['User']['email']);         if($model->save())             $this->redirect(array('view','id'=>$model->ID));         else             $model->password = $_POST['User']['password'];     }       $this->render('create',array(         'model'=>$model,     )); } ``` This way, we can create user with hashed password instead of plain password stored in our database. ## Customize Authentication - Component Next we need to authenticate our users. The original one just defined 'demo' and 'admin' as the only users that we are able to login. But now we have a database and a list of user and password. We should really secure our login. Here we modify our original authentication method to the following one. ```PHP public function authenticate() {     $username = $this->username;     $user = User::model()->find('username=?', array($username));     if($user === NULL)         $this->errorCode=self::ERROR_USERNAME_INVALID;     else if(!$user->validatePassword($this->password, $this->username))         $this->errorCode=self::ERROR_PASSWORD_INVALID;     else{         $this->username = $user->username;         $this->errorCode=self::ERROR_NONE;       }     return !$this->errorCode; } ``` this allowed us to go through the users in our database table instead of the hardcoded one by using the method we wrote previously on the modal folder. Now, our user will be authenticate using our customized authentication process rather than using the default one! No related posts. # Login via DB ### Maak een database tabel ```SQL CREATE TABLE `tbl_user` ( `id` int(11) NOT NULL, `username` varchar(128) NOT NULL, `password` varchar(128) NOT NULL, `authKey` varchar(200) DEFAULT NULL, `role` varchar(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ALTER TABLE `tbl_user` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `username` (`username`); ALTER TABLE `tbl_user` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; COMMIT; ``` ### Create CRUD voor tbl\_user [![image-1617048375766.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1617048375766.png) ](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1617048375766.png) [![image-1617048411848.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1617048411848.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1617048411848.png) ``` app\models\TblUser app\models\TblUserSearch app\controllers\TblUserController ``` ### Pas actionCreate voor TblUserController aan ```PHP public function actionCreate() { $model = new tblUser(); if ($model->load(Yii::$app->request->post()) ) { $model->password=sha1($model->password); $model->authKey=md5(openssl_random_pseudo_bytes(40)); if ($model->save() ) { return $this->redirect(['view', 'id' => $model->id]); } } return $this->render('create', [ 'model' => $model, ]); } ``` Dit zorgt ervoor dat er - een hash wordt gegenereerd als er een nieuwe user wordt aangemaakt - het password wordt gehashed ### Pas model User.php aan ```PHP 128], [['role'], 'string', 'max' => 20], ]; } public function attributeLabels() { return [ 'id' => 'ID', 'username' => 'Username', 'password' => 'Password', 'role' => 'Role', 'email' => 'Email', ]; } public static function findIdentity($id) { return static::findOne($id); } public static function findIdentityByAccessToken($token, $type = null) { return static::findOne(['access_token' => $token]); } public static function findByUsername($username) { return static::findOne(['username' => $username]); } public function getId() { return $this->id; } public function getAuthKey() { return $this->authKey; } public function validateAuthKey($authKey) { return $this->authKey === $authKey; } public function validatePassword($password) { return $this->password === sha1($password); } } ``` ### Users aanmaken met CRUD tbl\_user Let op: ### Get role ```PHP Yii::$app->user->identity->role ``` ### ToDo User can change his password -> new form and access based on Yii:$app->user->identity->username # Set Flash ### Set Flash Terugkoppeling vanuit de controller naar de view. #### In Controller ```PHP Yii::$app->session->setFlash('success', "Exam deleted."); of Yii::$app->session->setFlash('error', "Exam cannot be deleten, it has still forms attached to it."); ``` #### In View ``` session->hasFlash('success')): ?>
session->getFlash('success') ?>
session->hasFlash('error')): ?>
session->getFlash('error') ?>
``` xxx # Opdracht - Voetbal Toernooi 1\. Bij de onderstaande opdracht ga je een kleine applicatie die een voetbaltournooi probeert na te bootsen. Je kunt wedstrijden bekijken, opslaan, wijzigen en verwijderen. Elke wedstrijd bestaat uit twee teams en elk team bestaat uit meerdere spelers. 2\. Jouw opdracht is om de onderstaande ERD om te zetten in een CRUD applicatie. Let daarbij op de relaties tussen Team en Game 3\. Maak een nieuw Yii Project aan in ``` composer create-project --prefer-dist yiisoft/yii2-app-basic Tournament ``` 4\. Je mag de database zelf maken (goed om zelf te oefenenen). #### ERD [![image-1624353236435.png](http://roc.ovh/uploads/images/gallery/2021-06/scaled-1680-/image-1624353236435.png)](http://roc.ovh/uploads/images/gallery/2021-06/image-1624353236435.png) Repository: [https://github.com/ROC-van-Amsterdam-College-Amstelland/tournament-examen-oefening](https://github.com/ROC-van-Amsterdam-College-Amstelland/tournament-examen-oefening) # RESTfull API Om van een model een restfull API te maken, voeg de volgende controller toe: ```PHP '; } ?> ``` In de browser krijg je XML terug, test via Postman en je ziet dat je JSON terug krijgt. ##### Of plaats zelf een controller in ```PHP class HiscoresController extends Controller { /** * {@inheritdoc} */ public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ ], 'class' => '\yii\filters\Cors', 'cors' => [ 'Origin' => ['*'], 'Access-Control-Request-Method' => ['GET', 'POST'], 'Access-Control-Request-Headers' => ['*'], ], ], ]; } public static function allowedDomains() { return [ // '*', // star allows all domains 'http://localhost:3000', 'http://test2.example.com', ]; } public function beforeAction($action) { $this->enableCsrfValidation = false; return parent::beforeAction($action); } public function actionApiPost() { Yii::$app->response->format = \yii\web\Response:: FORMAT_JSON; $hiscore = new Hiscores(); $hiscore->scenario = Hiscores::SCENARIO_CREATE; $hiscore->attributes = yii::$app->request->post(); if($hiscore->validate()) { $hiscore->save(); } else { return array('status'=>false,'data'=>$hiscore->getErrors(), 'post'=>yii::$app->request->post(), 'get'=>yii::$app->request->get() ); } } public function actionApiGet() { $sql = "select id, name, score, datetime, ip from hiscores order by score desc"; $result = Yii::$app->db->createCommand($sql)->queryAll(); return json_encode(['data'=>$result]); // of json_encode([$result); } public function actionIndex() { ... // hieronder de standaard code ... ... ``` # Active - inactive zetten van een object Je wilt een onbect hidden maken of op een of andere manier uit zetten. ##### In de table maak je dan een extra kolom, bijvoorbeeld ``` actief, type boolean, default = 1 ``` ##### Dan pas je het model aan: ```PHP public function rules() {

...

[['grade'], 'integer'],

...

public function attributelabels()

...

'actief' => 'Actief' ``` ##### Code in de standaard grid view index ```PHP [

'attribute'=>'actief',

'contentOptions' => ['style' => 'width:10px;'],

'format' => 'raw',

'value' => function ($data) {

$status = $data->actief ? '✔' : '10060';

return Html::a($status, ['/student/toggle-actief?id='.$data->id],['title'=> 'Toggle Status',]);

}

], ``` ##### Functie/method in controller ```PHP public function actionToggleActief($id) {

// function toggles boolean actief

$sql="update student set active=abs(active-1) where id = :id;";

$params = array(':id'=> $id);

Yii::$app->db->createCommand($sql)->bindValues($params)->execute();

return $this->redirect(Yii::$app->request->referrer);

} ``` Laatset regel is een return naar de previous page. Dit zorgt ervoor dat de selectie in de grid view behouden blijft. Cést tout! # Dynamic return page In an update store referrer in Yii session and use it to return to when update is done. ```PHP // Store return URL, just before return to update view. Yii::$app->user->returnUrl = Yii::$app->request->referrer; // return to saved URL $returnUrl = Yii::$app->user->returnUrl ?: ['/aaa/bbb', 'id' => $id]; // if not saved go to default return $this->redirect($returnUrl); ``` # Grouping rows in Gridview Integrating collapsible groups with triangles in a Yii2 `GridView` requires a creative approach because `GridView` is primarily designed for flat data presentation. However, you can achieve this by manipulating the `GridView` and incorporating custom HTML and JavaScript for the collapsible functionality. Here’s a conceptual outline of how you might approach this: ### Step 1: Prepare Your Data Organize your data in a way that it can be grouped logically. Your data query should include the field(s) you intend to group by. ### Step 2: Customize Gridview Rows You'll use the `beforeRow` and `afterRow` properties of the `GridView` to inject custom HTML for group headers and footers. The group header will include the triangle and group title, and the group footer will serve as a marker for the end of a group. ##### PHP
```PHP echo GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'beforeRow' => function ($model, $key, $index, $grid) { static $lastGroup = null; $currentGroup = $model->groupAttribute; // Replace 'groupAttribute' with your actual attribute used for grouping if ($lastGroup !== $currentGroup) { $lastGroup = $currentGroup; return "
{$currentGroup}
"; } return null; }, // Define your columns, and other GridView configurations here... ]); ``` ### Step 3: CSS for Styling Add CSS for the group headers and the triangle icon. You can style the triangle to indicate collapsibility and use CSS transformations to change its orientation based on the group's collapsed state. ##### CSS
```CSS .group-header .triangle { cursor: pointer; display: inline-block; transition: transform 0.3s ease-in-out; } .group-header.collapsed .triangle { transform: rotate(-90deg); /* Pointing right when collapsed */ } .group-content { display: none; /* Initially hide the content */ } ``` ### Step 4: JavaScript for Collapse/Expand Functionality Use JavaScript to handle the click event on the group header, toggling the visibility of the group content and the orientation of the triangle icon. ##### JavaScript
``` $(document).on('click', '.group-header', function() { var header = $(this); header.toggleClass('collapsed'); header.nextUntil('.group-header').toggle(); // This will show/hide the rows until the next group header }); ``` ### Step 5: Integration with Gridview - **Group Content**: The actual rows of the `GridView` will serve as the group content. They will be shown/hidden based on the header click. - **Group Headers and Footers**: These are custom rows injected into the `GridView` using `beforeRow`. There's no built-in `afterRow`, but you can simulate this by checking if the next row belongs to a different group or if it's the last row of the current group. ### Caveats and Considerations - **Performance**: With many rows and groups, the performance might be impacted, especially client-side due to the large DOM manipulation. - **Complexity**: This solution adds a layer of complexity to your `GridView` rendering logic, so it's crucial to maintain clean and well-documented code. - **Styling and Behavior**: You might need to refine the CSS and JavaScript to fit the visual and functional requirements of your application. This approach is a bit of a hack around the default `GridView` behavior, so it requires careful implementation and testing to ensure it works smoothly across different datasets and user interactions. # Archive Oude lessen # 5, Zelf een view maken *DEZE EN VOLGENDE LESSEN WORDEN NOG AANGEPAST* *Tot nu hebben we gebruik gemaakt van de GridView om data te laten zien (localhost:8080/country/index) - in deze les gaan we onze eigen view maken. Het maken van een eigen view kost meer tijd, maar je kunt meer zaken zelf aanpassen zoals de weergave (formattering) van getallen.* ### Menu - Controller - View Om te beginnen maken we een nieuwe method in de **controllers/CountryControlle**r class. Deze controller noemen we actionOverzicht. Volgens de routing regels wordt de route wordt dus /country/overzicht.
##### Opdracht 1 Maar een menu item *overzicht* onder country dat naar */country/overzicht* gaat. [![image-1614620329168.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614620329168.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614620329168.png)
Probeer het menu uit. Wat zie je en kun je verklaren wat je ziet? Juist.... je krijgt een foutmelding want de routering verwijst naar de method/function actionOverzicht in de controller CountryController en die bestaat niet! We gaan dus een stukje code toevoegen aan onze CountryController. ```PHP use yii\data\Pagination; .. .. public function actionOverzicht() { // dit is de query, dit is te vergelijken met select * from Country $countries=Country::find()->all(); // de view wordt aangeroepen en het object $countries en $pagination wordt meegegeven. return $this->render('overzicht', [ 'countries' => $countries, ]); } ```
##### Opdracht 2 Maak de function *actionOverzicht* in de *CountryController* zoals hierboven is aangegeven.
Test opnieuw wat er gebeurt als je het menu *overzicht* selecteert ![image-1614623472969.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614623472969.png) What? Weer een fout? Waarom nu? Juist het menu gaat naar de actionOverzicht en de actionOverzicht voert een query uit en roept de view overzicht op. En die view bestaat (nog) niet. We gaan dus in de folder views/country een nieuwe file overzicht.php aanmaken en plaatsen daar de volgende code in. ```PHP Name; echo " - "; echo $country->Code; echo " - "; echo number_format($country->Population, 0, ',', ' '); echo "
"; } ?> ```
##### Opdracht 3 Maak de view *overzicht* zoals hierboven is aangegeven.
Het overzicht is bijna niet geformatteerd. [![image-1614627165703.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614627165703.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614627165703.png) Weet je nog hoe een HTML table er uit ziet? ```HTML
.. .. ..
.. .. ..
``` Hierboven staat een skelet van een table met twee rijen en drie kolommen.
##### Opdracht 4 Verander de /view/country/overzicht nu zo dat het netjes in een table wordt gezet. De output komt er als volgt uit te zien: [![image-1614627534845.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614627534845.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614627534845.png) De kolommen staan nu dus netjes onder elkaar.
Je hebt nu zelf een view opgebouwd zonder gebruik te maken van de Gridview widget. Het kost meer tijd en moeite om een overzicht te maken maar je hebt wel veel meer controle over hoe jouw overzicht er uit komt te zien. ### Sorteren en selecteren We gaan nu een paar functies bouwen die in de Gridview widget al ingebouwd zijn. Dit gaat om sorteren en selecteren. Kijk nog eens naar de *CountryController* bij de *actionOverzicht*, daar staat: `$countries=Country::find()->all();` Dit statement zoekt gaat naar het object country, dit staat in het model en zoekt daar alle regels (rows) in op. Yii vertaald dit nar de query `SELECT * FROM country` Maar we kunnen nog veel meer, kijk maar eens naar het volgende uitgebreide voorbeeld: ``` $countries = country::find()->where(['Continent' => 'Europe'])->orderBy(['Name' => SORT_ASC])->all(); ``` Hier worden alle *countries* geslecteerd met *Continent*=*Europe*, gesorteerd op *Name.* Het sql statement zou er zo uit zien: `SELECT * FROM country WHERE Continent = 'Europe' ORDER BY Name ASC` Op deze manier kun je dus via de code in de controller een selectie maken en de sortering aanpassen.
##### Opdracht 5 - - Maar een nieuw menu item en noem dit *Overzicht Europe*. - Maak een nieuw actionOverzichtEurope en koppel deze aan het menu item. (let op de vertaling naar de routering; in de routering staat nooit een hoofdletter en woorden worden gescheiden door - , zie les over routing). - Laat in dit overzicht alle landen van Europa zien, en druk de volgende kolommen af: *Name* en *Surface Area* - - Zet de twee kolommen netjes onder elkaar - Formateer de Surface Aera netjes met spatie tussen de duizendtallen en lijn ze recht uit. - Maak een header die bold is. - Sorteer het overzicht op Surface Area vangrootste land naar kleinste. Het overzicht kot er dus zo uit te zien: [![image-1614629726011.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1614629726011.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1614629726011.png)
Lees het commentaar in deze controller, zodat je begrijpt wat de verschillende blokken code doen. ```PHP use yii\data\Pagination; .. .. public function actionOverzicht() { $this->view->title = 'Search Countries'; // dit is de query, dit is te vergelijken met select * from Country $query=Country::find(); // hier worden bepaald hoeveel regels er op één pagina worden getoond en hoeveel regels er in totaal zijn $pagination = new Pagination([ 'defaultPageSize' => 5, 'totalCount' => $query->count(), ]); // hier wordt de query uitgevoerd, er wordt gesorteerd en er wordt slecht één pagina gelijktijdig binnen gehaald. $countries = $query->orderBy('name') ->offset($pagination->offset) ->limit($pagination->limit) ->all(); // de view wordt aangeroepen en het object $countries en $pagination wordt meegegeven. return $this->render('overzicht', [ 'countries' => $countries, 'pagination' => $pagination, ]); } ``` ### View In Yii zit standaard [bootstrap(3)](https://getbootstrap.com/docs/3.3/) geïnstalleerd. In de volgende view wordt gebruik gemaakt van [bootstrap(3) styles](https://getbootstrap.com/docs/3.3/). Dan zetten we in de file **views/country/overzicht.php** het volgende ```PHP

Countries

Land Code Hoofdstad Inwoners
Name ?> Code ?> capital) { // note: f.e. Antartica has no Capital echo $country->capital->Name; } else { echo ""; } ?> Population, 0, ',', ' '); ?>
$pagination]) ?> ``` In deze HTML-code onderscheiden we enkele zaken: 1. We hebben een table waarin we alles afdrukken. 2. We hebben een table header waarin we de kopjes van de verschillende kolommen afdrukken. 3. We hebben een loop waarmee we door het *country* object heen itereren en alle countries afdruken. 4. We hebben een widget die ervoor zorgt dat we naar de volgende pagina kunnen springen. **Opdrachten** 1. schrijf van de vier bovenstaande punten op waar je dit precies vind in de code, noteer de regels waarop het betrekking heeft. Bijvoorbeeld: 1 heeft betrekking op regel 10-32 2. wat (welk stukje code) zorgt ervoor dat het inwoneraantal netjes geformatteerd wordt afgedrukt? 3. In deze code wordt de naam van de hoofdstad afgedrukt (en niet meer de foreign key zoals in ons eerdere overzicht). Op welke regel wordt de naam van de hoofdstad afgedrukt? 4. Er staan 'landen' in de database die geen hoofdstad hebben zoals bijvoorbeeld Antartica. Verander de code, zodat er in deze gevallen een streepje (-) wordt afgedrukt. Opmerking: In de view zie je php-code staan tussen `` en tussen` ` Dit is bijna hetzelfde: `` is een verkorte notering voor ` 

` \-- # 6, De tweede web app *In deze les gaan we oefenen met alles wat we tot nu hebben geleerd. We zullen een database met een N:M relatie gebruiken en we zullen zien dat dat niet echt anders is als een 1:N relatie.* #### Stap 1, Database **In deze stap gaan we een nieuwe database maken.** Maak een nieuwe database; ga naar [deze pagina](https://www.roc.ovh/books/databases-1/page/student-%28test%29database) en installeer de student database. Je hebt nu een database student met drie tabellen; *student*, *vak* en een koppeltabel *cijfer.* Een student heeft meerdere vakken een één vak kan door meerdere studenten worden gevolgd. ![](https://www.roc.ovh/uploads/images/gallery/2020-07/image-1594496640728.png) #### Stap 2, Nieuw Yii project **In deze stap gaan we een nieuw Yii project maken.** ``` composer create-project --prefer-dist yiisoft/yii2-app-basic student ``` Pas de **config/web.php** en de **config/db.php** aan (kijk naar naar de [eerste les](https://www.roc.ovh/books/yii/page/de-eerste-web-app) als je niet meer weet hoe dit precies moet). *Tip: vergeet niet de localhost in de d.php te veranderen in 127.0.01* Start de Yii PHP server. #### Stap 2, Models **In deze stap gan we de models maken.** Maak met behulp van Gii zoals we dat in de eerste les hebben gedaan een model van alle *drie* de tabellen. Maak daarna met Gii een CRUD van de student tabel. Kijk nog een keer naar de [eerste les](https://www.roc.ovh/books/yii/page/de-eerste-web-app) als je niet meer weet hoe dit precies moet. #### Stap 3, CRUDs **In deze stap gaan we de CRUDs generen** Maak met Gii een CRUD van de student-tabel, de cijfer-tabel en de vak-tabel. Kijk nog een keer naar de [eerste les](https://www.roc.ovh/books/yii/page/de-eerste-web-app) als je niet meer weet hoe dit precies moet. #### Stap 4, View **In deze stap gaan we onze eigen view maken waarin we per student alle (behaalde) cijfers laten zien.** In deze stap zien we een relatie, namelijk een 1:N relatie. Elke student heeft immers 0,1, of meer vakken. In de vorige lessen hebben we hier mee geoefend, weet je nog? In Eén land werden er 1 of meer talen gesproken. In dat overzicht lieten we alle talen per land zien en nu laten we alle vakken per student zien. Technisch werkt dit op dezelfde manier. [![image-1594677185236.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594677185236.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594677185236.png) #### Stap 5, Update Cijfer **In deze stap gaan we een het automatisch gegenereerde form waarmee we een cijfer kunnen aanpassen veranderen.** In de standaard CRUD van de tabel cijfer kun je cijfers updaten. Ga naar [http://localhost:8080/cijfer](http://localhost:8080/cijfer) en druk op edit (rechts). Je komt nu in het update-scherm van de tabel cijfer. [![image-1594748309104.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594748309104.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594748309104.png) Nu zie je alleen de gegevens uit de tabel *cijfer*: je zou liever de naam van de student en de naam van het vak zien. Dat gaan we fixen! Allereerst kijken we naar de URL: [http://localhost:8080/cijfer/update?id=0](http://localhost:8080/cijfer/update?id=0) Om te bepalen welke view we moeten aanpassen volgend de routeringsregels. We gaan naar de *CijferController.php* file en zoeken naar de *actionUpdate*. We zien dat aan het einde van deze function (method) de view update wordt aangeroepen: ```PHP return $this->render('update', [ 'model' => $model, ]); } ``` De update view staat in *views/cijfer/update.php*, maar als je die opent, dan zie je heel weinig code. Dat komt, omdat de *update* en *insert* heel veel op elkaar lijken. Het invulscherm (form) is namelijk voor beide hetzelfde. En beide views (*update.php* en *insert.php*) roepen dus beiden *\_form.php* aan. Als we in *\_form.php* wijzigingen maken, dan gelden die dus voor update en voor insert. We willen dat het scherm er ongeveer zo gaat uitzien: [![image-1594750980851.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594750980851.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594750980851.png) We kunnen in dit form alleen het cijfer aanpassen; de naam van de student en de naam van het vak kunnen hier niet worden aangepast. Het cijfer staat overigens als 100-tal in de database. In dit voorbeeld staat 85 en dat staat voor een 8.5. <video uitleg - met gebruik van dd> ##### Stap 6, De link tussen overzicht en update Nu moeten we een link maken tussen ons overzichtsscherm en de juisten update pagina. Het moet je moet op het vak/cijfer van een student kunnen klikken en dan moet jet juiste update scherm worden getoond. Als we naar de link kijken die wordt gebruikt om een cijfer te updaten dan zien we dat daar een id wordt meegegeven. Dit is het ID van de tabel cijfer, de primary key. Om dus naar het juiste update-scherm te kunnen springen hebben we het ID van de *cijfer* tabel dus nodig. Ons overzicht *overzicht.php* staat in de student directory. Om zeker te weten dat we het juiste ID gebruiken, maken we een tijdelijke aanpassing in het overzicht en drukken het ID van de cijfer tabel af. **Opdracht (1):** druk na het *cijfer* in student/overzicht.php het id af van de koppeltabel waarin het cijfer voor het vak staat. Zie voorbeeld hieronder. [![image-1594752066080.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594752066080.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594752066080.png) We hebben nu alle informatie om van het cijfer een hyperlink te maken naar het juiste update scherm. **Opdracht (2)**: maak van het cijfer dat wordt afgedrukt in student/overzicht.php een hyperlink (<a href=....), zodat als je op het cijfer klikt, het juiste en zonet gemaakte update scherm verschijnt. Het skelet van de code om de hyperlink af te drukken: ```PHP > cijfer)/10,1) ?> ``` Om het overzichtelijk te houden maak je eerst de variabele $url met de juisten URL en maak je daarna de hyperlink. De XXXXXX moet nog worden vervangen door het juiste id (van de tabel vak). Deze heb je bij de vorige opdracht al laten afdrukken. \-- # 7, Sorteren, selectie maken en menu's In deze les.... #### Stap 1, aanmaken nieuwe overzicht We gaan in de *cijferController*, een nieuwe function (method) *actionOverzicht* maken. Als basis kun je de *actionOverzicht* uit *studentController* kopiëren. Je moet de nieuwe functie natuurlijk wel aanpassen! We gaan in de view *cijfer* een nieuw *overzicht.php* aanmaken. Als basis kun je de file *overzicht.php* uit student kopiëren. Ook deze nieuw gekopieerde view moet je wel aanpassen! We drukken de volgende vier kolommen af: ``` student->voornaam ?> student->achternaam ?> vak->naam ?> cijfer ?> ``` #### Stap 2, conditioneel afdrukken We gaan de cijfers conditioneel anders afdrukken. De conditie is dat we cijfers lager dan 55 in het rood gaan afdrukken. De conditie is dus *cijfer<55*. We kunnen hiervoor de volgende code gebruiken. ``` cijfer < 55): ?> cijfer)/10,1) ?> cijfer)/10,1) ?> ``` Op regel 1 staat de conditie, dit is bijna hetzelfde als een PHP if. Het enige verschil is dat de { is vervangen door een : Op regel 2 wordt het cijfer door 10 gedeeld en met één decimaal in het rood afgedrukt. Op regel 3 staat de *else*, hier wordt de } weggelaten en de { is vervangen door : Op regel 4 wordt het cijfer net zo afgedrukt als op regel 3, alleen nu niet in het rood. Op regel 5 staat een endif, zonder } en met een ;. De ; geeft aan dat dit het eidne is van de if. #### Stap 3, sortering aanpassen De sortering wordt geregeld vanuit de controller. De query wordt in de controller gemaakt. ``` $query=cijfer::find() ``` Voeg hier een *orderBy* aan toe. ``` $query=cijfer::find() ->orderBy('cijfer'); ``` #### Stap 3, selectie aanpassen We gaan een selectie maken op vak, dit gebeurt ook in de controller. We voegen een *where* toe. ``` $query=cijfer::find() ->where( ['vak_id' => $vak_id] ) ->orderBy('cijfer'); ``` Oops, waar komt $vak\_id vandaan? Hoe weet de controller waar hij op moet selecteren? Dat weet de controller nog niet, we zullen bij het aanroepen van controller een parameter moeten meegeven, verander de definitie van de controller in: ``` public function actionOverzicht($vak_id=1) ``` *$vak\_id* wordt nu optioneel meegegeven en is 1 als er geen waarde wordt meegegeven. Probeer maar, als het goed is, zie je nu alleen de vakken waarvan het *vak\_id* een 1 is (en dat in *Nederlands*). Verander de URL naar bijvoorbeeld: ``` http://localhost:8080/cijfer/overzicht/?vak_id=2 ``` en je ziet alleen het vak met het *vak\_id* 2 (en dat is *Engels*). #### Stap 4a, extra menu in overzicht [![image-1594845411296.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594845411296.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594845411296.png) De eenvoudigste methode om via de view een vak te selecteren is met een 'eenvoudig' Bootstrap menu. Dat kan met de volgende code: ```PHP ``` Dit is de Bootstrap manier om een menu te maken. Zie: [https://getbootstrap.com/2.3.2/components.html#buttonDropdowns](https://getbootstrap.com/2.3.2/components.html#buttonDropdowns) voor meer uitleg. Op zich hoef je niet precies te weten hoe het werkt, als je maar weet hoe je dit kunt aanpassen voor je eigen menu. Je kunt dit menu ook in de menu bar van de applicatie plaatsen, daarvoor moet je de file *views/layouts/mail.php* aanpassen. #### Stap 4b, tabbed menu in overzicht [![image-1594845470106.png](http://www.roc.ovh/uploads/images/gallery/2020-07/scaled-1680-/image-1594845470106.png)](http://www.roc.ovh/uploads/images/gallery/2020-07/image-1594845470106.png) Een tabbed overzicht ziet er fraaier, maar is wat lastiger. Dit kun je op de volgende manier toevoegen in je view. ```PHP ``` Dit is vrijwel allemaal PHP code. De vakken staan in een array en zijn in de database genummerd met de ID's 1 tot en met 9. In een loop worden alle menu items afgedrukt en wordt de juiste waarde uit het array erbij afgedrukt. Bestudeer de code en vraag om uitleg als je dit niet begrijpt. \-- # 8, Login / rollen *In onze tweede web app, de student database gaan we een login maken. Als je aanlogt als beheerder dan mag je de cijfers invoeren, veranderen of deleten.* ##### models/Users.php ``` private static $users = [ '100' => [ 'id' => '100', 'username' => 'admin', 'password' => 'admin', 'authKey' => 'test100key', 'accessToken' => '100-token', 'role'=> 'admin', ], '101' => [ 'id' => '101', 'username' => 'user', 'password' => 'demo', 'authKey' => 'test101key', 'accessToken' => '101-token', 'role'=> 'user', ], ]; ``` ##### In controller ```PHP public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => ['index','view'], 'allow' => true, 'roles' => ['@'] // any authenticated user, use ? for guests ], [ 'actions' => ['create','update','delete','overzicht'], 'allow' => true, 'roles' => ['@'], 'matchCallback' => function ($rule, $action) { return (Yii::$app->user->identity->role == 'admin'); } ], ], ], ]; } ``` ##### via Init method in Controller (replaces the \_\_construct) ```PHP // if user object does nit exists or it exists but is anything but admin, go to login screen public function init() { if (! isset(Yii::$app->user->identity->role) || Yii::$app->user->identity->role != 'admin') { $this->redirect(['/site/login']); } } ``` # Opdrachten #### Les 1, opdracht 1 1. Bespreek met elkaar wat een framework is en probeer een omschrijving te maken. 2. Bespreek met elkaar waarom jij een framework zou willen leren gebruiken? 3. Welke (PHP) frameworks ken je? --- #### Les 1, opdracht 2 #### Als jij een framework kiest om te leren, wat is dan belangrijk voor jou? Zet de volgende punten op volgorde van belangrijkheid (van meest belangrijk naar minder). - Veel over te vinden (tutorials e.d.). - Veel plugins voor beschikbaar. - Wordt actief onderhouden, veel updates dus. - Is makkelijk te leren, kun je makkelijk je examen mee halen. - Kun je snel iets mee maken/ontwikkelen. - Heeft heel veel mogelijkheden. - Genereert automatisch code, die je dan kan aanpassen. - Heeft veel ‘straight out of the box’, sorteren, selecteren, login, menu, etc. - Is hip/modern/cool. ---
#### Huiswerk, les 1 1. Lees de [Yii inleiding](https://www.roc.ovh/books/yii/page/inleiding) 2. Volg[ les 1](https://www.roc.ovh/books/yii/page/1-de-eerste-web-app) tot en met "Create New Yii project", Je hebt dan dus XAMPP draiaen, Composer geïnstalleerd en een nieuw Yii project gemaakt.
--- # CRUD generator issue In versie 2.0.43 van het Yii framework zit een foutje die soms optreed. Helaas treedt deze fout ook op bij de World database. ### Nieuwe fout (urlCreator) Deze fout is opgelost, maar er zit nu een andere fout in de Yii CRUD generator. Nadat de CRUD is gemaakt krijg je een foutmelding. Deze gaat over de index.php file van de view. In dit voorbeeld moet je het woord *Country* weghalen. [![image-1655735527573.png](https://www.roc.ovh/uploads/images/gallery/2022-06/scaled-1680-/image-1655735527573.png)](https://www.roc.ovh/uploads/images/gallery/2022-06/image-1655735527573.png) Of in zijn algemeenheid, stel je hebt een CRUD gemaakt voor *Abc* Vervang dan dit 'urlCreator' => function ($action, Abc $model, $key, $index, $column) { in 'urlCreator' => function ($action, $model, $key, $index, $column) { '*Abc*' is in dit voorbeeld weggehaald. (in de foutmelding staat het regelnummer en de locatie van de file waar deze regel staat). ### Wat is de fout? Als je op het overzicht (van bijvoorbeeld countries) op het oogje rechts op de pagina klikt, dan krijg je een foutmelding. *View*, *update* en *delete* werken niet of slecht gedeeltelijk. ### Wat is de oorzaak? Als de primary key van een tabel niet *id* heet dan kan het fout gaan. De fout zit in de crud generator template. ### Work around? Je kunt een oude crud generator installeren. Hieronder wordt uitgelegd hoe dat moet. ### Installeren oude CRUD generator template Lees elke stap goed en werk heel nauwkeurig, we gaan namelijk de werking van Yii hacken. We maken gebruik van de CRUD generator template 2.0.37 ##### Stap 1 [Download de zip](https://www.roc.ovh/attachments/63) file die in deze les staat. ##### Stap 2 Ga vanuit de project folder naar de volgende directory: ``` vendor\yiisoft\yii2-gii\src\generators ``` In deze folder zie je het volgende: [![image-1635448540144.png](https://www.roc.ovh/uploads/images/gallery/2021-10/scaled-1680-/image-1635448540144.png)](https://www.roc.ovh/uploads/images/gallery/2021-10/image-1635448540144.png) ##### Stap 3 Hernoem de *crud* folder in *crud.org,* en ##### Stap 4 zet de crud folder uit de zip file in de folder. Het ziet er dan zo uit: [![image-1635448675537.png](https://www.roc.ovh/uploads/images/gallery/2021-10/scaled-1680-/image-1635448675537.png)](https://www.roc.ovh/uploads/images/gallery/2021-10/image-1635448675537.png) ##### Stap 5 Maak via de crud generator opnieuw de crud. Eventuele aanpassingen worden dan overschreven. # Create simple report with export CSV This is generic Yii code to generate a simple report from an SQL query. The query has no parameters. ### QueryController.php The last method/function needs to be changed. The method can also be copied as much as needed. Change the code that is labeled with < and > in the last function. Open the controller the usual way: query/<name of action> ```PHP [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], 'access' => [ 'class' => AccessControl::className(), 'rules' => [ // when logged in, any user [ 'actions' => [], 'allow' => true, 'roles' => ['@'], ], ], ], ]; } private function executeQuery($sql, $title="no title", $export=false) { $result = Yii::$app->db->createCommand($sql)->queryAll(); if ($result) { // column names are derived from query results $data['col']=array_keys($result[0]); } $data['row']=$result; if ($export) { $this->exportExcel($data); } else { $data['title']=$title; return $data; } } public function exportExcel($data) { header('Content-type: text/csv'); header('Content-Disposition: attachment; filename="canvas-export' . date('YmdHi') .'.csv"'); foreach ($data['col'] as $key => $value) { echo $value.", "; } echo "\n"; foreach ($data['row'] as $line) { foreach ( $line as $key => $value) { echo $value.", "; } echo "\n"; } } public function action($sort='desc', $export=false) { // sort parameter is optional, export parameter determines if export to CSV needs to be given // specify query $sql="select column1 Header1, column2 Header2,.... from .... order by 3 $sort"; // specify report title $data=$this->executeQuery($sql, , $export); // specify sub title (descr, can also be omitted). return $this->render('output', [ 'data' => $data, 'action' => Yii::$app->controller->action->id, 'descr' => , ]); } } ``` ### View The view does not need any adaptations. ```HTML

".$descr.""; } ?>
'btn btn-primary', 'title'=> 'Export to CSV',]) ?>

#"; if ( $data['row'] ) { for($i=0;$i".$data['col'][$i].""; } } else { echo ""; } ?> "; echo ""; } for($i=0;$i".$item[$data['col'][$i]].""; } echo ""; } } ?>
Empty result set
".$nr."
``` # 09, Form en drop down *We gaan onze koffie applicatie uitbreiden.* *Om het invoeren van gegevens eenvoudiger te maken gaan we drop down menu's toevoegen.* *We gaan naar twee soorten drop-downs kijken; die met vaste waarden en die met dynamische waarden (uit de database).* *Bij het invoeren van een bestelling gaan we allereerst de status via een drop down invoeren.* *Daarna gaan we waarde voor de medewerker ook uit een drop down halen. Deze waarde moet uit de database komen.* ### Drop down in Bestelling We gaan kijken naar de bestelling tabel en CRUD. Als we de status in de database als een enum hebben aangemaakt dan weet Yii dat de status uit beperkt aantal waarden kan hebben en als het goed is maakt Yii dan vanzelf een een drop down in het form. Klopt dat heb jij een drop down? Indien niet, dan kun je de code aanpassen in het form aanpassen. In de view *\_form.php* van Bestellingen moet de volgende regel staan: ```PHP field($model, 'status')->dropDownList([ 'besteld' => 'Besteld', 'klaar' => 'Klaar', 'betaald' => 'Betaald', '' => '', ], ['prompt' => '']) ?> ``` Deze regel laat een drop down menu in het formulier zien. Kijk eens goed naar de parameter van dropDownList. ``` [ 'besteld' => 'Besteld', 'klaar' => 'Klaar', 'betaald' => 'Betaald', '' => '', ], ['prompt' => ''] ``` De eerste parameter is een associative array waarin elk element bestaat uit een key en een value. De key is de waarde die het element uit het form krijgt (de value) en deze waarde wordt door Yii in de database gezet. De key komt dus overeen met de waarde in de database. De value van het associatieve array is wat de gebruiker op het scherm ziet. Deze waarde kan je dus veranderen. Je kunt dus bijvoorbeeld 'betaald' veranderen in 'afgerekend'. Het enige dat dan gebeurt is dat je iets ander op het scherm ziet. In dit voorbeeld is de key en de value gelijk (de waarde op het scherm is hetzelfde als de waarde in de database). Dus samengevat, de key is de waarde in de database en de value is wat de gebruiker op het scherm ziet. Stel we willen in het form van bestelling de medewerker veranderen. Dan moeten we dus de *foreign key* die naar medewerker verwijst veranderen. Het juiste id van de medewerker moet in de tabel bestelling worden ingevuld. De waarde wordt dus het id, maar dat is niet wat je de gebruiker wilt laten zien. Dus als we voor de medewerkers een drop down willen maken dan hebben we een assiociative array nodig dat er zo uit ziet: ``` [ '1'=> 'Ayoub', '2'=> 'Brahim','3'=> 'Carla','4'=> 'Diego','5'=> 'Eisa' ] ``` De keys zijn de id's die als foreign keys in de bestelling tabel staan en de namen zijn de namen van de medewerkers. ### Opdracht 8a Maak nu een statische drop down met de waarden zoals in het voorbeeld (Ayoub, Brahim, Carla, ....). De waarden worden dus (nog) niet uit de database gehaald. ##### Inleveren 1. Schermafdruk yii-08a-jouw-naam met het form waarin je de drop down (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. ### Opdracht 8b, Dynamische drop down In opdracht 8a hebben we een drop down van medewerkers gemaakt, maar we willen de lijst van medewerkers natuurlijk uit de database halen. *In deze opdracht leg ik stap-voor-stap uit wat je moet doen omdat oor elkaar te krijgen. Lees alles aandachtig door en sla geen stappen over!* Data uit de database halen doen we in de controller. Open de BestellingController. Vanuit deze code wordt de create view aangeroepen en vanuit de create view wordt de \_form.php aangeroepen. Waarom dit in twee stappen gaat leggen we later uit. We veranderen de code in Bestelling controller: ```PHP use app\models\Medewerker; .. .. public function actionCreate() { $model = new Bestelling(); $medewerkers = Medewerker::find()->all(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', [ 'model' => $model, 'medewerkers' => $medewerkers ]); } ``` De eerste regel `use app\models\Medewerker;` zetten we bovenaan in de BestellingController. Hiermee vertellen we Yii dat vanuit de *BestellingController* gebruik willen gaan maken van het model *Medewerker*. In de function *actionCreate* die wordt aangeroepen als we een nieuwe bestelling willen maken, voegen we een object toe waarin alle medewerkers zitten. In regel 9 halen we alle medewerkers op en in regel 17 sturen we het resultaat naar de view create.php (van *bestelling*). We kunnen de medewerkers ook ophalen via een sql-query. Je mag regel 9 ook vervangen in de volgende twee regels: ```PHP // dit is hetzelfde als // $medewerkers = Medewerker::find()->all(); $sql="select * from medewerker"; $medewerkers = Yii::$app->db->createCommand($sql)->queryAll(); ``` In de view create.php van bestelling passen we het laatste code aan ```PHP render('_form', [ 'model' => $model, 'medewerkers' => $medewerkers ]) ?> ``` Hiermee geven we het object medewerkers weer door aan de view *\_form* van bestelling. In het view \_form zetten we nu bovenaan in het PHP-gedeelte. ```PHP dd($medewerkers); ``` Dit is de debugfunctie en hiermee controleren we of we inderdaad alle medewerkers naar de view hebben gestuurd. We hebben nu als het goed is een lijst van medewerkers in de \_form maar we moeten het ombouwen naar een assiociative array. Pas hiervoor de code aan in de view \_form van bestelling. ```PHP ... ... ... ``` Deze code laat het eerst stukje van de view\_form zien. Met de functie ArrayHelper::map zetten we het object $medewerkers om in een assiociative array. Met dd() laten we dat zien. Probeer maar! Als het goed is, zie je het volgende: ```PHP [ 1 => 'Ayoub' 2 => 'Brahim' 3 => 'Carla' 4 => 'Diego' 5 => 'Eisa' ] ``` En dit is precies wat we nodig hebben om de Drop Down te maken. Pas de regel in de view \_form van bestelling waarin de user het id van de medewerker moet intypen aan. Verander deze regel in: ```PHP field($model, 'medewerker_id')->dropDownList($medewerkerList, ['prompt' => ''])->label('Medwerker') ?> ``` Als het goed is heb je hiermee een werkend menu gekregen. [![image-1616605065315.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616605065315.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616605065315.png) Gelukt? Wordt de lisjt van medewerkers in de drop down uit de database gehaald? ### Inleveren 1. schermafdruk yii-08b-jouw-naam met het form waarin je de drop down van de medewerkers (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. ### Opdracht 8c
Maak nu een drop down voor de bestelling zodat de medewerker uit een lijstje van koffiesoorten kan kiezen bij het opnemen van de bestelling. [![image-1616605448841.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616605448841.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616605448841.png) Gebruik hiervoor het stappenplan dat hieronder is beschreven.
#### Het stappenplan voor een drop down Schrijf eerst het volgende op: In welk model komt de drop down? Dit noemen we de target. (in opdracht 2 hierboven is dat *Bestelling*) Waar komt de informatie voor de Drop Down uit? Dit noemen we de source. (in opdracht 2 hierboven is dat *Menu*) #### Voer nu de stappen 1-6 uit 1. Zet in de target controller welke source model we willen gebruiken. ``` // in controller van target use app\models\; ``` 2. Haal in de target controller in de actionCreate() de informatie uit de source op. ``` // sourceName bedenk jij zelf en daarin staat de informatie die in de drop down moet komen. $sourceName = source::find()->all(); ``` 3. Geef vanuit de target controller in de actionCreate() de informatie door aan de view. ``` return $this->render('create', [ 'sourceName' => $sourceName, .... ]); ``` 4. Open de view create in de target (view) en geef de informatie door aan \_form view. ``` render('_form', [ 'sourceName' => $sourceName, ... ]); ``` 5. Open de view \_form in de target (view) en zet de informatie om in een assiociative array ``` // in dit voorbeeld worden id en naam gebruikt: // id is het veld dat moet worden opgeslagen // naam is het veld dat in de drop te zien is $sourceNameList = ArrayHelper::map($sourceName,'id','naam'); ``` 6. Pas het invoerveld aan in de \_form van de target (view). ``` // veld_in_de_db is het veld van de target dat in dit invoerveld wordt weggeschreven. field($model, 'veld_in_de_db')->dropDownList($$sourceNameList, ['prompt' => '']) ?> ``` Als je alles correct gedaan hebt dan moet je nog 1 regel aanpassen in de code om de functionaliteit te gebruiken. Yii controleert automatisch of alle databasekolommen zijn ingevuld in het formulier. Dit controleren wordt *validatie* genoemd. Deze regels staan in het model. Omdat je geen Bestelling ID opgeeft bij het maken van een bestelling moet je deze requirement verwijderen op regel 33 van model Bestelling. ```PHP return [ [ ['naam', 'medewerker_id', 'menu_id', 'status'], 'required'], ... ... ] ``` ### Inleveren 1. schermafdruk yii-08c-jouw-naam met het form waarin je de drop down van de producten (opengeklapt) laat zien. Maak een schermafdruk van je gehele browser. 2. de *BestellingController.php* en zet jouw naam bij de aanpassingen die je hebt gedaan. ### Opdracht 8d De insert en update lijken op elkaar en gebruiken hetzelfde form (\_form.php). We hebben nu de insert aangepast, maar de update nog niet. Controleer maar! We moeten nu de update dus ook nog aanpassen. #### aanpassen update view Weet je nog dat de -update view niet rechtstreeks door de actionCreate van de BestellingController wordt aangeroepen? Hoe zit dat nu? [![image-1616610519616.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616610519616.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616610519616.png) De *actionCreate* en *actionUpdate vanuit* de controller gaan bieden naar hun 'eigen' view maar via deze eigen view komen beide op hetzelfde form uit. Dat komt omdat een *update* en *create* hetzelfde form gebruiken. Als we nu dus een update uitvoeren van de bestelling (pennetje vanuit de gridview) dan krijgen we een foutmelding. Dat komt omdat de form verwacht dat er gegevens voor de drop down worden meegestuurd. Om dit te fixen kopiëren we de code in de controller die we in de *actionCreate* hebben gemaakt dus naar de *actionUpdate*. Deze twee functies zijn bijna hetzelfde. Bij de create wordt alleen een nieuw object gemaakt en bij de update wordt een bestaand object ingeladen. Nu moeten we de objecten nog doorgeven van de *update* view naar de *\_form* view. Net zoals we dat ook deden bij de *create.*
Zorg ervoor dat de update weer werkt en dat de objecten op de juiste manier worden doorgegeven. Lees hiervoor de uitleg die hierboven staat. Pas de actionUpdate in de BestellingController aan. Pas de update view van de BestellingController aan Zorg dat de update van een Besteliing goed werkt..
### Inleveren 1. de *BestellingController.php* en zet jouw naam bij de aanpassingen die je hebt gedaan. ### ### Opdracht 8e #### De final touch Laten we nog wat labels aanpassen. Dit zijn de teksten die boven de invoervelden in het form staan.
Pas de labels in het form aan zodat er Medewerker, Klantnaam, Bestelling en Status Bestelling komt te staan: [![image-1616611684520.png](https://www.roc.ovh/uploads/images/gallery/2021-03/scaled-1680-/image-1616611684520.png)](https://www.roc.ovh/uploads/images/gallery/2021-03/image-1616611684520.png) Zoek zelf uit hoe dit moet. Tip gebruik de zoektermen: **How to change label text of the ActiveField?**
### Inleveren 1. schermafdruk yii-08e-jouw-naam met het bestellingen form waarin je de aangepaste labels laat zien. Maak een schermafdruk van je gehele browser. \-- # Yii Cheat Sheet ### Back ```PHP  request->referrer , ['class'=>'btn btn-primary']); ?> ``` ### Grid View ```PHP [ 'attribute' => 'no_answered', 'label' => '#vragen', 'enableSorting' => false, 'filter' => false, 'headerOptions' => ['style' => 'width:60px;'], 'contentOptions' => function ($model, $key, $index, $column) { if ( ! $model->finished )return ['style' => ""]; $score = $model->no_questions > 0 ? round(($model->no_correct / $model->no_questions) * 100, 0) : 0; if ( $model->no_answered ) { $backgroundColor = $score < 55 ? 'lightcoral' : 'lightgreen'; } else { $backgroundColor = ""; } return ['style' => "background-color: $backgroundColor;"]; }, 'format' => 'raw', 'value' => function ($model) { return Html::a($model->no_answered.'/'.$model->no_questions, ['submission/update', 'id' => $model->id]); return $model->no_answered.'/'.$model->no_questions; }, ], ['class' => 'yii\grid\SerialColumn'], ``` ```PHP [ 'attribute' => 'label', 'label' => 'Label', 'headerOptions' => ['style' => 'width:200px;'], 'contentOptions' => [ 'style' => 'color: #404080;' ], ``` xxx