--- title: Laravel [02] (Una app CRUD) tags: daw, Laravel, migrations, M7 --- <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licencia de Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a><br />Este obra está bajo una <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">licencia de Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional</a>. [Enllaç a Hackmk.io](https://hackmd.io/@JdaXaviQ/H1m28_2-n) ## Laravel [02] (Relacions 1a1). ### Migracions amb relacions 1-1: ![](https://i.imgur.com/QVBtB25.png) Eloquent: l'ORM que utilitza Laravel, ens permet definir relacions entre taules; com ja sabem, les relacions poden ser 1-1, 1-N o N-N. En aquesta sessió aprendrem a definir relacions 1-1 entre les nostres taules directament des del nostre projecte Laravel sense haver de tocar ni una sola sentència SQL i per tant de forma totalment independent del sistema gestor de base de dades triat. Eloquent encara no està preparat per a treballar amb claus foranes compostes, de manera que totes les nostres taules hauran de tenir una clau primària d'un sol camp. Enlloc de trastejar directament amb la nostra aplicació CRUD, explicarem el concepte amb un projecte nou de proves i un cop assolits els conceptes, passarem a implementar la nostra primera relació entre taules a la app principal. El nostre projecte de proves s'anomenarà relacions1a1 i ho crearem directament des de la consola, aprofitarem també per a crear una base de dades i passar-li tots els permisos a l'usuari laravel que ja tenim creat: ```bash= isard@ubuntu:~/src$ composer create-project laravel/laravel relacions1a1 isard@ubuntu:~/src$ cd relacions1a1 isard@ubuntu:~/src/relacions1a1$ sudo mysql Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 31 Server version: 10.6.12-MariaDB-0ubuntu0.22.04.1 Ubuntu 22.04 Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> create database relacions; Query OK, 1 row affected (0,001 sec) MariaDB [(none)]> grant all privileges on relacions.* to laravel@localhost; Query OK, 0 rows affected (0,005 sec) MariaDB [(none)]> flush privileges; Query OK, 0 rows affected (0,001 sec) ``` Ara ja podem modificar el fitxer .env per a configurar l'accés a la base de dades: ```yaml= DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=relacions DB_USERNAME=laravel DB_PASSWORD=1234 ``` A continuació crearem la nostra migració per a la taula capitals: ```bash= isard@ubuntu:~/src/relacions1a1$ php artisan make:migration create_capitals_table INFO Migration [database/migrations/2023_04_06_171429_create_capitals_table.php] created successfully. ``` I modifiquem la funció up() per a que contingui els camps que dessitjem: ```php= public function up(): void { Schema::create('capitals', function (Blueprint $table) { $table->id(); $table->string('nom'); $table->integer('poblacio'); $table->timestamps(); }); } ``` De manera anàloga creem també la migració per a la taula pais: ```bash= isard@ubuntu:~/src/relacions1a1$ php artisan make:migration create_pais_table INFO Migration [database/migrations/2023_04_06_172039_create_pais_table.php] created successfully. ``` I relitzem les modificacions pertinents: ```php= public function up(): void { Schema::create('pais', function (Blueprint $table) { $table->id(); $table->char('codi', 3); $table->string('nom'); $table->string('continent'); $table->string('regio'); $table->float('superficie', 10, 2); $table->integer('poblacio'); $table->unsignedBigInteger('capital_id'); $table->foreign('capital_id')->references('id')->on('capitals')->onDelete('cascade'); $table->timestamps(); }); } ``` :eye: Compte! el tipus de dades de capital_id és __unsignedBigInteger__ i hem definit la relació de clau forànea amb la id de la taula 'capitals'. Per defecte, la polĺitica seria 'restrict' així que la canviem a 'cascade' tot i que també podriem utlitzar 'set null'. El següent pas lògic seria realitzar les migracions per materialitzar les consegüents taules a la base de dades: ```bash= isard@ubuntu:~/src/relacions1a1$ php artisan migrate INFO Preparing database. Creating migration table ......................................... 24ms DONE INFO Running migrations. 2014_10_12_000000_create_users_table ............................. 45ms DONE 2014_10_12_100000_create_password_reset_tokens_table ............. 45ms DONE 2019_08_19_000000_create_failed_jobs_table ....................... 35ms DONE 2019_12_14_000001_create_personal_access_tokens_table ............ 50ms DONE 2023_04_06_171429_create_capitals_table .......................... 10ms DONE 2023_04_06_172039_create_pais_table .............................. 54ms DONE ``` Verifiqueu que les taules estan creades i que s'ha establert la relació entre capitals i pais i podem fer proves important el contingut del fitxer 'capital_inserts.sql' que podeu trobar al Moodle del mòdul. > mysql -u laravel -p relacions < ~/Baixades/capitals_inserts.sql Ara el pas lògic següent és generar els models: > php artisan make:model Capital > php artisan make:model Pais En aquest punt ens sorgeix un petit problema, la taula de països la hem anomenat: __pais__ i al model __Pais__ i per tant hem de definir el nom de la taula dins del model per a que Laravel sigui capaç de trobar-la correctament. ```php= <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Pais extends Model { protected $table = 'pais'; use HasFactory; } ``` :eye: :fire: Les funcions que ens permeten rel·lacionar els dos models són respectivament en una rel·lació 1a1 són: * hasOne(Model) * belongsTo(Model) I és important conèixer el sentit d'aquesta rel·lació. Com a regla general, al model de la taula que no conté la clau forànea, afegim la funció _hasOne_ i al que si conté la clau forànea la funció _belongsTo_ ```php= <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Capital extends Model { use HasFactory; public function pais() { return $this->hasOne(Pais::class); } } ``` ```php= <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Pais extends Model { use HasFactory; protected $table = "pais"; public function capital() { return $this->belongsTo(Capital::class); } } ``` Ara és el moment de definir les rutes que permeten accedir a la informació que tenim emmagatzemada a la nostra base de dades (de moment només afegirem la ruta GET): ```php= <?php use Illuminate\Support\Facades\Route; use App\Models\Pais; use App\Models\Capital; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider and all of them will | be assigned to the "web" middleware group. Make something great! | */ Route::get('/', function () { return view('welcome'); }); Route::get('/capital/{id}', function ($id) { return Capital::find($id); }); Route::get('/pais/{id}', function ($id) { return Pais::find($id); }); ``` Que ens proporcionen els següents resultats: ```bash= isard@ubuntu:~$ curl -X GET http://localhost:8000/pais/1 {"id":1,"codi":"AFG","nom":"Afghanistan","continent":"Asia","regio":"Southern and Central Asia","superficie":652090,"poblacio":22720000,"capital_id":1,"created_at":null,"updated_at":null} isard@ubuntu:~$ curl -X GET http://localhost:8000/capital/1 {"id":1,"nom":"Kabul","poblacio":1780000,"created_at":null,"updated_at":null} ``` I també podem ampliar els end-points, per a trobar països a partir de la id de la capital o a l'inrevés: ```php= <?php use Illuminate\Support\Facades\Route; use App\Models\Pais; use App\Models\Capital; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider and all of them will | be assigned to the "web" middleware group. Make something great! | */ Route::get('/', function () { return view('welcome'); }); Route::get('/capital/{id}', function ($id) { return Capital::find($id); }); Route::get('/pais/{id}', function ($id) { return Pais::find($id); }); Route::get('/capital/{id}/pais', function ($id) { return Capital::find($id)->pais; }); Route::get('/pais/{id}/capital', function ($id) { return Pais::find($id)->capital; }); ``` ```bash= isard@ubuntu:~$ curl -X GET http://localhost:8000/capital/1/pais {"id":1,"codi":"AFG","nom":"Afghanistan","continent":"Asia","regio":"Southern and Central Asia","superficie":652090,"poblacio":22720000,"capital_id":1,"created_at":null,"updated_at":null} isard@ubuntu:~$ curl -X GET http://localhost:8000/pais/1/capital {"id":1,"nom":"Kabul","poblacio":1780000,"created_at":null,"updated_at":null} ```