Jimmy Buriticá Londoño
Uriel Rodolfo Ortíz Popolo
Gran aporte bro :D
Edgar Ramón González Contreras
No me muestra nada :(
Levanta rápido una API de productos en Laravel usando SQLite, migraciones, TDD, Faker, model factories y seeders. Sin servidores extra ni configuraciones complejas: configura la base de datos, genera datos de prueba y valida con tests automatizados. Ideal para un flujo ágil y confiable.
¿Cómo configurar SQLite en Laravel sin servidor?
Configurar una base transaccional liviana con SQLite evita instalar y levantar servicios. Laravel trae el driver listo y se activa con unos ajustes mínimos.
¿Qué cambiar en .env y en config database.php?
database/database.sqlite..env, ajustar la conexión: DB_CONNECTION=sqlite.DB_DATABASE, borrarla para usar el archivo por defecto.config/database.php: el driver sqlite usa database/database.sqlite si no hay DB_DATABASE.¿Cómo ejecutar migraciones con php artisan migrate?
php artisan migrate
DB_DATABASE en .env y repetir el comando.database/database.sqlite. Probar conexión: debe decir que está OK.¿Cómo generar datos de prueba con Faker, factories y seeders?
Para trabajar la API, se requieren registros confiables y repetibles. Aquí entran Faker, model factories y seeders.
¿Qué hace un model factory con Faker?
database/factories, ajustar el factory de producto:
nombre: con Faker, un nombre al azar.precio: un número entre diez mil y sesenta mil.¿Cómo se ejecuta un seeder para poblar la base?
ProductSeeder, usar el factory del modelo para crear N registros y create para insertarlos.DatabaseSeeder, llamar a ProductSeeder para centralizar su ejecución.php artisan db seed
¿Cómo construir y probar la API de productos con TDD?
La técnica TDD guía el desarrollo: primero fallan los tests, luego se implementa lo mínimo para hacerlos pasar. Así se asegura funcionalidad y cobertura desde el inicio.
¿Qué crear con make model y controlador API?
php artisan make model product
products como nombre (string) y precio (float); luego migrar para crear la tabla.¿Cómo funcionan index, store, show, update y destroy?
index: listar todos los productos con Eloquent y retornarlos.store: crear con Product::create(...) usando los atributos de la petición.show: gracias a la inyección de dependencias, recibe el modelo y lo retorna.update: actualizar el recurso con update(...) desde la petición.destroy: eliminar el producto y responder en JSON con 200.Product, declarar protected $guarded = []; para permitir crear/actualizar con cualquier atributo permitido.¿Cómo declarar rutas y verificar con row list?
routes/api.php, definir una ruta tipo recurso con la URI products apuntando al controlador.php artisan row:list
Habilidades y conceptos reforzados en este flujo:
.env para la conexión de BD.$guarded.¿Te animas a replicarlo con un modelo Category? Crea su model, factory, seeder, migration y su endpoint para crear y listar categorías. Cuéntame en comentarios cómo te fue y qué dudas te surgieron.
Jimmy Buriticá Londoño
Uriel Rodolfo Ortíz Popolo
Gran aporte bro :D
Edgar Ramón González Contreras
No me muestra nada :(
Sergio Andrés Martínez Ramírez
Para los que usen Visual Studio Code, existe una extensión llamada SQLite, que nos permite visualizar la base de datos y ejecutar queries desde el editor.
IRIDIAN GUADALUPE CARRERA MONDRAGÓN
una pregunta compañero, cómo haces para que la extensión te permita visualizar los archivos .sqlite? yo ya la instale pero mi archivo se sigue viendo con binarios.
Leonardo Gomez
Esta es la ext y podemos ejecutar los query desde allí
Para ejecutar los tests, para aquellos que no tengan phpstorm, lo pueden ejecutar desde la consola en la raíz del proyecto con el comando:
php artisan test
O también pueden ejecutar directamente php unit con el siguiente comando:
vendor/bin/phpunit
Además si quieren ver mejor la salid de esta forma, pueden agregar el parámetro --testdox
vendor/bin/phpunit --testdox
php artisan test
esto funciona solo en laravel ^7.0
Carlos Eduardo Gomez García
En Laravel 8, los models factories cambian, ahí se debe usar:
Product::factory(5)->create()
Y para usar faker en un archivo de Factory debes accederlo mediante $this : $this->faker->name
Les dejo los archivos completos de esta clase en este commit:
Diego Alejandro Toro Reyes
Cristian Pisco Intriago
Juan Vallejos
Licencia gratuita por un año de php storm para estudiantes. https://www.jetbrains.com/es-es/community/education/#students Licencia gratuita por un año de navicat(magnifico gestor de bases de datos) para estudiantes. https://www.navicat.com/es/sponsorship/education/student.html
Earvin Saúl Pérez Ramos
A mi me daba error al migrar Illuminate\Database\QueryException could not find driver (SQL: PRAGMA foreign_keys = ON;)
La solución
sudo apt-get install php-sqlite3
Cristian Rafael Cala Sierra
Solucionó mi migración! buenisimo
Mauricio Avila Beltran
Usando Laravel 8, el sistema de rutas tambien varia, por lo que en nuestro archivo routes/api.php quedaria asi:
use App\Http\Controllers\ProductController; use App\Http\Controllers\CategoryController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); Route::resource('products', ProductController::class); Route::resource('categories', CategoryController::class);
Marco Antonio Castillo Delgado
En el método test_update_product del ProductControllerTest me salía el siguiente error:
"1) Tests\Feature\ProductControllerTest::test_update_product Header [content-type] was found, but value [text/html; charset=UTF-8] does not match [application/json]. Failed asserting that two strings are equal."
Se soluciona agregando los headers directamente en la función de jsonPatch y no en una línea extra como en el ejemplo:
-------------------------------------------- ANTES ----------------------------------------------------- $response = $this->patchJson("/api/products/{$product->getKey()}", $data); $response->assertHeader('content-type', 'application/json'); $response->assertSuccessful();
------------------------------------------- DESPUÉS ---------------------------------------------------- $response = $this->patchJson("/api/products/{$product->getKey()}", $data, ['content-type', 'application/json']); $response->assertSuccessful();
Mariano Gastón Paduani
Muchas gracias por el aporte!!!!
Fernando Mejia
Jesebell Maderleyn Escobar Hurtado
Muchas gracias por tu aporte!
Tomas Goldenberg
que le cambia al Model el flag --api?
Fernando Mejia
cuando creas un modelo usando el flag --api crea un controller de tipo resource pero solo con los metodos:
los metodos show y create no se crean porque se da por entendido que al ser una API no vas a usar archivos blade para mostrar vistas
Leonardo Gomez
Los Metodos usados.
Mauricio Galvez
Christian Luis Soria
muchas gracias, definitivamente me habia dado cuenta cuando creo los datos falsos en el factory que la sintaxis es distinta a laravel 8.
Fernando Mejia
Si alguien tiene problemas con los tests una manera sencilla de debuggear es implementando el metodo getContent:
dd(json_decode($response->getContent()));
Con esto pueden obtener la salida de su response, si alguien no tiene configurado PHPStorm puede usar tambien el comando:
php artisan test
Si alguien no tiene PHPStorm como editor puede probarlo gratis por 30 dias o si tiene un correo educativo solicitar la licencia para estudiante, si quieren saber mas de esta herramienta tambien les recomiendo en youtube la playlist de PHPStorm de Christopher Rumpel.
Alejandro Valenzuela
Tengo una consulta, es posible poder configurar sqlite para realizar los test y tener otra base de dato(mysql o postgres) para el sistema??
Tony Manuel Ojeda Vela
Alejandro Valenzuela
Gracias tony-ojeda por tu comentario. Saludos
Fabian Andres Soler Cruz
No se deberían usar para este curso software licenciado como lo es Php Storm, ya que todos no tienen esa posibilidad.
Hector Rubio
Es un curso avanzado, ya en este punto deberiamos saber manejar laravel, independientemente el editor de texto
Juan David Rueda Quiroga
Para Laravel 10.x en lugar de usar en los test assertDeleted usen assertModelMissing
Josue Miguel Prieto Mojica
hidelberg martinez
No me gusta que el curso sea en laravel 7, ahorita lo estoy haciendo y me da error al crear el proyecto en dicha version, ademas en laravel 8 donde lo estoy haciendo da errores...
Actualicen si son tan amables..
Milán Gutiérrez Jáuregui
Todo mal!!!!, Les recomiendo que tomen otro curso, por que el que lo dicta, no explica casi nada (bueno se sabe que este curso es 'avanzando', pero aun asi debe tomar detalle en algunos temas y explicarlo), solo copia y pega desde otro proyecto, perdida de tiempo con este curso.
lucas cartisano
Totalmente de acuerdo
Marco Antonio Castillo Delgado
No supe como ejecutar los test con el debugger de Visual Studio Code y busque otra manera, si tienes el mismo problema que yo puedes utilizar el comando "php vendor/phpunit/phpunit/phpunit --filter test_update_product ProductController tests\Feature\ProductControllerTest.php" para ejecutar cada uno de los test en Windows: php vendor/phpunit/phpunit/phpunit/ para ejectura todos los test. Puedes especificar la clase y que método en especifico quieres ejecutar con la etiqueta --filter <método_de_la_clase> <dirección_del_<archivo>.
Rodrigo Ortega
Si les ocurre un error en el test en el apartado "update" es porque faltó el return
public function update(Request $request, Product $product) { $product->update($request->all()); return $product; }```
Estoy usando Laravel v8.4 y el factory y seed son un poco diferentes, deben hacerlo así:
ProductFactory.php
public function definition() { return [ 'name' => $this->faker->name, 'price' => $this->faker->numberBetween(10000, 60000) ]; }
ProductSeeder.php
public function run() { \App\Models\Product::factory(10)->create(); }
Hola consulta, vi en tu código test que al método test_index de ProductController justo en el método assertJsonCount, aparte de que te cuente los registros, le pasas el parámetro key como "data", al menos por mi parte me da un error, entonces quiero saber que estoy haciendo mal.
Para que me funcionara solo debí omitir la key, pero quiero entender si es algo que yo estoy haciendo mal.
tu codigo
public function test_index() { Product::factory(5)->create(); $response = $this->getJson('/api/products'); $response->assertSuccessful(); $response->assertHeader('content-type', 'application/json'); $response->assertJsonCount(5, "data"); }
mi codigo
public function test_index() { Product::factory(5)->create(); $response = $this->getJson('/api/products'); $response->assertSuccessful(); $response->assertHeader('content-type', 'application/json'); $response->assertJsonCount(5); }
El parámetro "data" se utilizará más adelante. En laravel una API se trabaja con una capa de transformación que se llama "Resource" en los próximos videos el profesor explicará sobre ese tema, lo que hace es convertir un modelo o colección de modelos Eloquent en Json. Por el momento no le coloques el argumento "data". Ese parámetro es un json que contiene lo siguiente:
{ "data": { { "name": "Product 1", "price": 20 }, { "name": "Product 2", "price": 20 } } }
Aqui la clase para hacer el test con los metodos, me tome la libertad de hacer algunos ligeros cambios para hacer mas comodo el test y en algunos metodo solo cambie la forma de traer el response para fines ilustrativos:
<?php namespace Tests\Feature; use App\Product; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Tests\TestCase; class APIProductControllerTest extends TestCase { use RefreshDatabase; const API_HEADER_NAME = 'content-type'; const API_HEADER_VALUE = 'application/json'; protected $data = [ 'name' => 'Product one', 'price' => 1000 ]; public function setUp(): void { parent::setUp(); // TODO: Change the autogenerated stub } /** * A basic feature test example. * * @test */ public function test_index() { factory(Product::class)->times(5)->create(); $response = $this->json('get', '/api/products/'); $response->assertSuccessful(); $response->assertHeader(self::API_HEADER_NAME, self::API_HEADER_VALUE); $response->assertJsonCount(5); } /** @test */ public function test_create_new_product() { $response = $this->json('post', '/api/products', $this->data); $response->assertSuccessful(); $response->assertHeader(self::API_HEADER_NAME, self::API_HEADER_VALUE); $this->assertDatabaseCount('products', 1); } /**@test */ public function test_update_product() { $product = factory(Product::class)->create(); $response = $this->patchJson("/api/products/{$product->getKey()}", $this->data); $response->assertSuccessful(); // $response->assertHeader(self::API_HEADER_NAME, self::API_HEADER_VALUE); $this->assertEquals($this->data['name'], Product::first()->name); } /**@test */ public function test_show_product() { $product = factory(Product::class)->create(); $response = $this->getJson(route('products.show', $product)); $response->assertSuccessful(); $response->assertHeader(self::API_HEADER_NAME, self::API_HEADER_VALUE); $this->assertEquals($product->name, $response->json('name')); } /**@test */ public function test_delete_product() { $product = factory(Product::class)->create(); $response = $this->deleteJson(route('products.destroy', $product)); $response->assertSuccessful(); $response->assertHeader(self::API_HEADER_NAME, self::API_HEADER_VALUE); $this->assertDatabaseCount('products', 0); }
Código del test en Laravel 8
<?php namespace Tests\Feature; use App\Models\Product; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class ProductControllerTest extends TestCase { use RefreshDatabase; protected function setUp(): void { parent::setUp(); } public function test_index() { $product = Product::factory()->count(5)->create(); $response = $this->json('GET', '/api/products'); $response ->assertSuccessful() ->assertHeader('content-type', 'application/json') ->assertJsonCount(5); } public function test_create_new_product() { $data = [ 'name' => 'Hola', 'price' => 1000, ]; $response = $this->json('POST', '/api/products', $data); $response ->assertSuccessful() ->assertHeader('content-type', 'application/json'); $this->assertDatabaseHas('products', $data); } public function test_update_product() { /** @var Product $product */ $product = Product::factory()->create(); $data = [ 'name' => 'Update Product', 'price' => 20000, ]; $response = $this->json('PATCH', "/api/products/{$product->getKey()}", $data); $response ->assertSuccessful() ->assertHeader('content-type', 'application/json'); } public function test_show_product() { /** @var Product $product */ $product = Product::factory()->create();; $response = $this->json('GET', "/api/products/{$product->getKey()}"); $response ->assertSuccessful() ->assertHeader('content-type', 'application/json'); } public function test_delete_product() { /** @var Product $product */ $product = Product::factory()->create(); $response = $this->deleteJson("/api/products/{$product->getKey()}"); $response ->assertSuccessful() ->assertHeader('content-type', 'application/json'); $this->assertDeleted($product); } }
en el archivo phpunit.xml toma por defecto el archivo database/database.sqlite, lo que normalmente se usa para los test.
En la carpeta config/database.php, se modifica:
'sqlite' => [ 'driver' => 'sqlite', 'url' => env('DATABASE_URL'), 'database' => database_path('database.sqlite'), 'prefix' => '', 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), ],
en tu .env, agregas el de mysql:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=api_platzi DB_USERNAME=root DB_PASSWORD=root
AGOSTO 2022 El proyecto los comandos para la creación de la base de datos siguen igual. Uso la versión 9.3.4 de Laravel
Para ProductFactorie.php
class ProductFactory extends Factory { /** * Define the model's default state. * * @return array<string, mixed> */ public function definition() { return [ 'name' => $this->faker->name, 'price' => $this->faker->numberBetween(1000, 60000) ]; } }
Para ProductSeeder.php
class ProductSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Product::factory(10)->create(); }
Y el comando que deben de correr crear los datos:
php artisan db:seed --class ProductSeeder
Para la API deben descomentar una linea en app > Http > Kernel.php > Kernel
[...] 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], [...]
Luego deben ir a routes/api.php y escribiir en las rutas
Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); Route::resource('Product', ProductController::class);
Y el test lo tuve que modificar, y todavía no llego a la verificación del JSON pero hice lo que pude:
public function test_index(){ Product::factory(5)->create(); $response = $this->getJson('/api/Product'); $response->assertSuccessful(); }