Getting started after deploying LEMP
Conexión al droplet por ssh ( con un usuario diferente a root:):
ssh usuario@alumno.me
How to install all required PHP extensions for Laravel 8?
sudo apt install openssl php-common php-curl php-json php-mbstring php-mysql php-xml php-zip
sudo apt install openssl php-common php-curl php-json php-mbstring php-mysql php-xml php-zip
sudo apt install openssl php-common php-curl php-json php-mbstring php-mysql php-xml php-zip
Installation of PHP Extensions for Laravel 9
sudo apt install php8.2-common php8.2-mysql php8.2-xml php8.2-xmlrpc php8.2-curl php8.2-gd php8.2-imagick php8.2-cli php8.2-dev php8.2-imap php8.2-mbstring php8.2-opcache php8.2-soap php8.2-zip php8.2-intl -y
sudo apt install php8.2-common php8.2-mysql php8.2-xml php8.2-xmlrpc php8.2-curl php8.2-gd php8.2-imagick php8.2-cli php8.2-dev php8.2-imap php8.2-mbstring php8.2-opcache php8.2-soap php8.2-zip php8.2-intl -y
sudo apt install php8.2-common php8.2-mysql php8.2-xml php8.2-xmlrpc php8.2-curl php8.2-gd php8.2-imagick php8.2-cli php8.2-dev php8.2-imap php8.2-mbstring php8.2-opcache php8.2-soap php8.2-zip php8.2-intl -y
Server configuration
3. Instalación de Composer y node.js
How to Install Composer in Ubuntu 22.04 LTS
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
composer -V
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
composer -V
Instalar node.js y npm en Ubuntu 20.04
Installation instructions
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs
#Using Ubuntu
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs
node -v
npm -v
#Using Ubuntu
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs
node -v
npm -v
4. Instalación de Laravel
(Eliminar la carpeta todo, si ya estaba creada)
# borrar la carpeta todo, si existe
cd ~
# borrar la carpeta todo, si existe
rm -r todo
cd ~
# borrar la carpeta todo, si existe
rm -r todo
Installation Via Composer
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Usando el instalador de Laravel
composer global require laravel/installer
composer global require laravel/installer
laravel new todo
composer global require laravel/installer
laravel new todo
(command not found)
https://stackoverflow.com/questions/61395786/i-get-laravel-command-not-found-on-ubuntu-20-04
A) Make sure you have the latest Laravel installer:
composer global require laravel/installer
B) Add composer bin folder to your $PATH:
Edit your .bashrc file:
nano $HOME/.bashrc
Add the following line:
export PATH="$PATH:$HOME/.config/composer/vendor/bin"
C) Use the source command to force Ubuntu to reload your .bashrc file:
source $HOME/.bashrc
D) Try to output Laravel installer’s version:
laravel -V
laravel new todo
┌ Would you like to install a starter kit? ────────────────────┐
│ Laravel Jetstream │
└──────────────────────────────────────────────────────────────┘
┌ Which Jetstream stack would you like to install? ────────────┐
│ Livewire │
└──────────────────────────────────────────────────────────────┘
┌ Would you like any optional features? ───────────────────────┐
│ API support │
│ Dark mode │
│ Email verification │
└──────────────────────────────────────────────────────────────┘
┌ Which testing framework do you prefer? ──────────────────────┐
│ ○ Pest │
│ › ● PHPUnit │
└─────────────────
┌ Which database will your application use? ───────────────────┐
│ MySQL │
└──────────────────────────────────────────────────────────────┘
┌ Default database updated. Would you like to run the default database migrations? ┐
│ Yes │
└──────────────────────────────────────────────────────────────────────────────────┘
Error en el droplet con 1 Gb de RAM (son necesarios 2 Gb de RAM, como mínimo):
Redimensionar el droplet con 2 Gb de memoria RAM (cuando se termine la instalación se puede volver a redimensionar a 1 Gb):

5. Base de datos
Crear la base de datos todoDB y el usuario user_todoDB con la password Malaga2324*
Conectarse al servidor de bases de datos
sudo mysql
Ejecutar estos comandos:
CREATE USER 'user_todoDB'@'localhost' IDENTIFIED BY 'Malaga2324*';
GRANT ALL PRIVILEGES ON todoDB.* TO 'user_todoDB'@'localhost';
CREATE DATABASE todoDB;
CREATE USER 'user_todoDB'@'localhost' IDENTIFIED BY 'Malaga2324*';
GRANT ALL PRIVILEGES ON todoDB.* TO 'user_todoDB'@'localhost';
FLUSH PRIVILEGES;
exit
CREATE DATABASE todoDB;
CREATE USER 'user_todoDB'@'localhost' IDENTIFIED BY 'Malaga2324*';
GRANT ALL PRIVILEGES ON todoDB.* TO 'user_todoDB'@'localhost';
FLUSH PRIVILEGES;
exit
Modificar el fichero .env:
nano .env
Información de conexión a la base de datos:
DB_COLLATION=utf8mb4_unicode_ci
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todoDB
DB_USERNAME=user_todoDB
DB_PASSWORD=Malaga2324*
DB_COLLATION=utf8mb4_unicode_ci
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todoDB
DB_USERNAME=user_todoDB
DB_PASSWORD=Malaga2324*
DB_COLLATION=utf8mb4_unicode_ci
Instalar phpMyAdmin en el VPS
sudo apt install phpmyadmin
sudo apt install php8.0-mbstring
sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin
sudo systemctl restart nginx
sudo systemctl status nginx
ssh usuario@alumno.me
sudo apt install phpmyadmin
sudo apt install php8.0-mbstring
sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl status nginx
exit
ssh usuario@alumno.me
sudo apt install phpmyadmin
sudo apt install php8.0-mbstring
sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl status nginx
exit
Extensión Database Client de Visual Studio Code
Instalar la extensión Database Client
Configurar la extensión en las pestañas main y ssh

6. Prueba
(php artisan migrate)
Cambiar el propietario de la carpeta storage y bootstrap/cache:
sudo chown -R www-data:www-data storage
sudo chown -R www-data:www-data bootstrap/cache
cd ~/todo
sudo chown -R www-data:www-data storage
sudo chown -R www-data:www-data bootstrap/cache
cd ~/todo
sudo chown -R www-data:www-data storage
sudo chown -R www-data:www-data bootstrap/cache
Ejecutar
npm install
npm run build
npm install
npm run build
Comprobación:
laravel.alumno.me

7. Montar en el equipo local el sistema de ficheros remoto del VPS
Uso en Linux
Nota: Estos comandos se ejecutan en el equipo local, no en el VPS

mkdir servidor
sudo apt install sshfs
sshfs usuario@alumno.me:/home/usuario/todo/ servidor -p 22
# Abrir la carpeta con el IDE Visual Studio Code
(Instalar exensión Laravel Extra Intellisense
Instalar exensión PHP Intellisense)
cd servidor
code .
Para desmontarlo:
fusermount -u servidor
Usar la extensión sshfs de Visual Studio Code:
Instalar la extensión SSH FS
Configurarla con el host, ruta a la carpeta remota y usuario

8. MVC en Laravel

9. Creación de la aplicación CRUD
Migraciones:
php artisan make:migration create_tasks_table --create=tasks
php artisan make:migration create_tasks_table --create=tasks
php artisan make:migration create_tasks_table --create=tasks
database/migrations/2024_05_07_084758_create_tasks_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
public function up(): void
Schema::create('tasks', function (Blueprint $table) {
$table->string('description');
$table->integer('user_id')->unsigned()->index();
* Reverse the migrations.
public function down(): void
Schema::dropIfExists('tasks');
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('description');
$table->integer('user_id')->unsigned()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('description');
$table->integer('user_id')->unsigned()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
php artisan migrate
Migrar una sola tabla:
php artisan migrate --path=/database/migrations/2024_05_07_084758_create_tasks_table.php
php artisan migrate --path=/database/migrations/2024_05_07_084758_create_tasks_table.php
php artisan migrate --path=/database/migrations/2024_05_07_084758_create_tasks_table.php
Modelo:
php artisan make:model Task
php artisan make:model Task
php artisan make:model Task
Relación uno a muchos
app/Models/Task.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
protected $fillable = ['description'];
return $this->belongsTo(User::class);
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
class Task extends Model
{
use HasFactory;
protected $fillable = ['description'];
public function user()
{
return $this->belongsTo(User::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
class Task extends Model
{
use HasFactory;
protected $fillable = ['description'];
public function user()
{
return $this->belongsTo(User::class);
}
}
app/Models/User.php
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
use TwoFactorAuthenticatable;
* The attributes that are mass assignable.
* @var array<int, string>
* The attributes that should be hidden for serialization.
* @var array<int, string>
'two_factor_recovery_codes',
* The accessors to append to the model's array form.
* @var array<int, string>
* Get the attributes that should be cast.
* @return array<string, string>
protected function casts(): array
'email_verified_at' => 'datetime',
return $this->hasMany(Task::class);
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use App\Models\Task;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function tasks()
{
return $this->hasMany(Task::class);
}
}
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use App\Models\Task;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function tasks()
{
return $this->hasMany(Task::class);
}
}
Rutas
routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TasksController;
Route::get('/', function () {
config('jetstream.auth_session'),
//Route::get('/dashboard', function () {
// return view('dashboard');
Route::get('/dashboard',[TasksController::class, 'index'])->name('dashboard');
Route::get('/task',[TasksController::class, 'add']);
Route::post('/task',[TasksController::class, 'create']);
Route::get('/task/{task}', [TasksController::class, 'edit']);
Route::post('/task/{task}', [TasksController::class, 'update']);
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TasksController;
Route::get('/', function () {
return view('welcome');
});
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
//Route::get('/dashboard', function () {
// return view('dashboard');
//})->name('dashboard');
Route::get('/dashboard',[TasksController::class, 'index'])->name('dashboard');
Route::get('/task',[TasksController::class, 'add']);
Route::post('/task',[TasksController::class, 'create']);
Route::get('/task/{task}', [TasksController::class, 'edit']);
Route::post('/task/{task}', [TasksController::class, 'update']);
});
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TasksController;
Route::get('/', function () {
return view('welcome');
});
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
//Route::get('/dashboard', function () {
// return view('dashboard');
//})->name('dashboard');
Route::get('/dashboard',[TasksController::class, 'index'])->name('dashboard');
Route::get('/task',[TasksController::class, 'add']);
Route::post('/task',[TasksController::class, 'create']);
Route::get('/task/{task}', [TasksController::class, 'edit']);
Route::post('/task/{task}', [TasksController::class, 'update']);
});
Vistas
Qué es TailwindCSS
Blade, Bootstrap y TailwindCSS
Se encuentran en resources/views
- dashboard.blade.php ( Dashboard muestra la lista de tareas)
- add.blade.php (Formulario para añadir una nueva tarea)
- edit.blade.php (Formulario para añadir una nueva tarea)
- welcome.blade.php (página inicial)
resources/views/dashboard.blade.php
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<table class="w-full text-md rounded mb-4">
<div class="flex-auto text-left text-2xl mt-4 mb-4 dark:text-white">Tasks List</div>
<th><div class="flex-auto text-right float-right mt-4 mb-4">
<a href="/task" class="bg-blue-500 dark:bg-cyan-700 hover:bg-gray-700 text-white font-bold mr-8 py-2 px-4 rounded">Add a new Task</a>
<tr class="border-b dark:text-white text-center">
<th class="text-left p-3 px-5">Task</th>
<th class="text-left p-3 px-5">Actions</th>
@foreach(auth()->user()->tasks as $task)
<tr class="border-b hover:bg-orange-100 dark:text-white text-center">
<form action="/task/{{$task->id}}" class="inline-block">
<a href="/task/{{$task->id}}" name="edit" class="text-sm bg-gray-500 hover:bg-gray-700 text-white py-1 px-4 rounded focus:outline-none focus:shadow-outline">Edit</a>
<button type="submit" name="delete" formmethod="POST" class="text-sm bg-red-500 hover:bg-red-700 text-white py-1 px-2 rounded focus:outline-none focus:shadow-outline">Delete</button>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<table class="w-full text-md rounded mb-4">
<thead>
<tr>
<th>
<div class="flex-auto text-left text-2xl mt-4 mb-4 dark:text-white">Tasks List</div>
</th>
<th><div class="flex-auto text-right float-right mt-4 mb-4">
<a href="/task" class="bg-blue-500 dark:bg-cyan-700 hover:bg-gray-700 text-white font-bold mr-8 py-2 px-4 rounded">Add a new Task</a>
</div></th>
</tr>
<tr class="border-b dark:text-white text-center">
<th class="text-left p-3 px-5">Task</th>
<th class="text-left p-3 px-5">Actions</th>
</tr>
</thead>
<tbody>
@foreach(auth()->user()->tasks as $task)
<tr class="border-b hover:bg-orange-100 dark:text-white text-center">
<td class="p-3 px-5">
{{$task->description}}
</td>
<td class="p-3 px-5">
<form action="/task/{{$task->id}}" class="inline-block">
<a href="/task/{{$task->id}}" name="edit" class="text-sm bg-gray-500 hover:bg-gray-700 text-white py-1 px-4 rounded focus:outline-none focus:shadow-outline">Edit</a>
<button type="submit" name="delete" formmethod="POST" class="text-sm bg-red-500 hover:bg-red-700 text-white py-1 px-2 rounded focus:outline-none focus:shadow-outline">Delete</button>
{{ csrf_field() }}
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<table class="w-full text-md rounded mb-4">
<thead>
<tr>
<th>
<div class="flex-auto text-left text-2xl mt-4 mb-4 dark:text-white">Tasks List</div>
</th>
<th><div class="flex-auto text-right float-right mt-4 mb-4">
<a href="/task" class="bg-blue-500 dark:bg-cyan-700 hover:bg-gray-700 text-white font-bold mr-8 py-2 px-4 rounded">Add a new Task</a>
</div></th>
</tr>
<tr class="border-b dark:text-white text-center">
<th class="text-left p-3 px-5">Task</th>
<th class="text-left p-3 px-5">Actions</th>
</tr>
</thead>
<tbody>
@foreach(auth()->user()->tasks as $task)
<tr class="border-b hover:bg-orange-100 dark:text-white text-center">
<td class="p-3 px-5">
{{$task->description}}
</td>
<td class="p-3 px-5">
<form action="/task/{{$task->id}}" class="inline-block">
<a href="/task/{{$task->id}}" name="edit" class="text-sm bg-gray-500 hover:bg-gray-700 text-white py-1 px-4 rounded focus:outline-none focus:shadow-outline">Edit</a>
<button type="submit" name="delete" formmethod="POST" class="text-sm bg-red-500 hover:bg-red-700 text-white py-1 px-2 rounded focus:outline-none focus:shadow-outline">Delete</button>
{{ csrf_field() }}
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</x-app-layout>
resources/views/add.blade.php
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white" placeholder='Enter your task'></textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-gray-500">{{ $errors->first('description') }}</span>
<button type="submit" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Add Task</button>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Add Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task">
<div class="form-group">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white" placeholder='Enter your task'></textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-gray-500">{{ $errors->first('description') }}</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Add Task</button>
</div>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Add Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task">
<div class="form-group">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white" placeholder='Enter your task'></textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-gray-500">{{ $errors->first('description') }}</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Add Task</button>
</div>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</x-app-layout>
resources/views/edit.blade.php
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task/{{ $task->id }}">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white">{{$task->description }}</textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-red-500">{{ $errors->first('description') }}</span>
<button type="submit" name="update" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Update task</button>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Edit Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task/{{ $task->id }}">
<div class="form-group">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white">{{$task->description }}</textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-red-500">{{ $errors->first('description') }}</span>
@endif
</div>
<div class="form-group">
<button type="submit" name="update" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Update task</button>
</div>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</x-app-layout>
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Edit Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-5">
<form method="POST" action="/task/{{ $task->id }}">
<div class="form-group">
<textarea name="description" class="bg-gray-100 dark:text-white dark:bg-gray-800 rounded border border-gray-400 leading-normal resize-none w-full h-20 py-2 px-3 font-medium placeholder-gray-700 focus:outline-none focus:bg-white">{{$task->description }}</textarea>
@if ($errors->has('description'))
<span class="text-danger dark:text-white dark:bg-red-500">{{ $errors->first('description') }}</span>
@endif
</div>
<div class="form-group">
<button type="submit" name="update" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Update task</button>
</div>
{{ csrf_field() }}
</form>
</div>
</div>
</div>
</x-app-layout>
public/css/styles.css (obtenido de welcome.blade.php)
nano public/css/styles.css
Contenido del fichero:
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}}
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}}
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}}
fichero public/css/colors.css
{background-color:#66bbe3}
{background-color:#1f93c9}
{background-color:#e37966}
{background-color:#b73f24}
.bg-blue-500
{background-color:#66bbe3}
.bg-blue-700
{background-color:#1f93c9}
.bg-red-500
{background-color:#e37966}
.bg-red-700
{background-color:#b73f24}
.bg-blue-500
{background-color:#66bbe3}
.bg-blue-700
{background-color:#1f93c9}
.bg-red-500
{background-color:#e37966}
.bg-red-700
{background-color:#b73f24}
resources/views/layouts/app.blade.css
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/colors.css">
@vite(['resources/css/app.css', 'resources/js/app.js'])
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
@livewire('navigation-menu')
<header class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/colors.css">
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<!-- Styles -->
@livewireStyles
</head>
<body class="font-sans antialiased">
<x-banner />
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
@livewire('navigation-menu')
<!-- Page Heading -->
@if (isset($header))
<header class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
@endif
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
@stack('modals')
@livewireScripts
</body>
</html>
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/colors.css">
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<!-- Styles -->
@livewireStyles
</head>
<body class="font-sans antialiased">
<x-banner />
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">
@livewire('navigation-menu')
<!-- Page Heading -->
@if (isset($header))
<header class="bg-white dark:bg-gray-800 shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
@endif
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
@stack('modals')
@livewireScripts
</body>
</html>
Añadir Bootstrap
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
resources/views/welcome.blade.php
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css -->
<link rel="stylesheet" href="css/styles.css">
<!-- <link rel="stylesheet" href="https://raw.githubusercontent.com/necolas/normalize.css/master/normalize.css"> -->
font-family: 'Nunito', sans-serif;
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
@if (Route::has('login'))
<div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
<a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 underline">Dashboard</a>
<a href="{{ route('login') }}" class="text-sm text-gray-700 underline dark:text-white">Log in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline dark:text-white">Register</a>
<div class="max-w-4xl mx-auto sm:px-6 lg:px-4">
<div class="flex justify-center text-primary">
<div class="flex justify-center text-success">
<h3>Manage your tasks online</h3>
<div class="flex justify-center text-secondary">
<div class="flex justify-center">
<div class="ml-4 text-center text-sm text-gray-500">
Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- Styles -->
<!-- normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css -->
<link rel="stylesheet" href="css/styles.css">
<!-- <link rel="stylesheet" href="https://raw.githubusercontent.com/necolas/normalize.css/master/normalize.css"> -->
<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
@if (Route::has('login'))
<div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
@auth
<a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 underline">Dashboard</a>
@else
<a href="{{ route('login') }}" class="text-sm text-gray-700 underline dark:text-white">Log in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline dark:text-white">Register</a>
@endif
@endauth
</div>
@endif
<div class="max-w-4xl mx-auto sm:px-6 lg:px-4">
<div class="flex justify-center text-primary">
<h1>Todo Tasks</h1>
</div>
<div class="flex justify-center text-success">
<h3>Manage your tasks online</h3>
</div>
<div class="flex justify-center text-secondary">
<h4>info@alumno.me</h4>
</div>
<div class="flex justify-center">
<div class="ml-4 text-center text-sm text-gray-500">
Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
</div>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- Styles -->
<!-- normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css -->
<link rel="stylesheet" href="css/styles.css">
<!-- <link rel="stylesheet" href="https://raw.githubusercontent.com/necolas/normalize.css/master/normalize.css"> -->
<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
@if (Route::has('login'))
<div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
@auth
<a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 underline">Dashboard</a>
@else
<a href="{{ route('login') }}" class="text-sm text-gray-700 underline dark:text-white">Log in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline dark:text-white">Register</a>
@endif
@endauth
</div>
@endif
<div class="max-w-4xl mx-auto sm:px-6 lg:px-4">
<div class="flex justify-center text-primary">
<h1>Todo Tasks</h1>
</div>
<div class="flex justify-center text-success">
<h3>Manage your tasks online</h3>
</div>
<div class="flex justify-center text-secondary">
<h4>info@alumno.me</h4>
</div>
<div class="flex justify-center">
<div class="ml-4 text-center text-sm text-gray-500">
Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
</div>
</div>
</div>
</div>
</body>
</html>
Controlador
php artisan make:controller TasksController
php artisan make:controller TasksController
php artisan make:controller TasksController
app/Http/Controllers/TasksController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TasksController extends Controller
$tasks = auth()->user()->tasks();
// return view('dashboard', compact('tasks'));
return view('dashboard')->with('tasks', $tasks);
public function create(Request $request)
//$this->validate($request, [
// 'description' => 'required'
// Validar los datos del formulario
'description' => 'required'
$task->description = $request->description;
$task->user_id = auth()->user()->id;
return redirect('/dashboard');
public function edit(Task $task)
if (auth()->user()->id == $task->user_id)
return view('edit', compact('task'));
return redirect('/dashboard');
public function update(Request $request, Task $task)
if(isset($_POST['delete'])) {
return redirect('/dashboard');
//$this->validate($request, [
// 'description' => 'required'
'description' => 'required'
$task->description = $request->description;
return redirect('/dashboard');
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
class TasksController extends Controller
{
public function index()
{
$tasks = auth()->user()->tasks();
// return view('dashboard', compact('tasks'));
return view('dashboard')->with('tasks', $tasks);
}
public function add()
{
return view('add');
}
public function create(Request $request)
{
//$this->validate($request, [
// 'description' => 'required'
//]);
// Validar los datos del formulario
$request->validate([
'description' => 'required'
]);
$task = new Task();
$task->description = $request->description;
$task->user_id = auth()->user()->id;
$task->save();
return redirect('/dashboard');
}
public function edit(Task $task)
{
if (auth()->user()->id == $task->user_id)
{
return view('edit', compact('task'));
}
else {
return redirect('/dashboard');
}
}
public function update(Request $request, Task $task)
{
if(isset($_POST['delete'])) {
$task->delete();
return redirect('/dashboard');
}
else
{
//$this->validate($request, [
// 'description' => 'required'
//]);
$request->validate([
'description' => 'required'
]);
$task->description = $request->description;
$task->save();
return redirect('/dashboard');
}
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Task;
class TasksController extends Controller
{
public function index()
{
$tasks = auth()->user()->tasks();
// return view('dashboard', compact('tasks'));
return view('dashboard')->with('tasks', $tasks);
}
public function add()
{
return view('add');
}
public function create(Request $request)
{
//$this->validate($request, [
// 'description' => 'required'
//]);
// Validar los datos del formulario
$request->validate([
'description' => 'required'
]);
$task = new Task();
$task->description = $request->description;
$task->user_id = auth()->user()->id;
$task->save();
return redirect('/dashboard');
}
public function edit(Task $task)
{
if (auth()->user()->id == $task->user_id)
{
return view('edit', compact('task'));
}
else {
return redirect('/dashboard');
}
}
public function update(Request $request, Task $task)
{
if(isset($_POST['delete'])) {
$task->delete();
return redirect('/dashboard');
}
else
{
//$this->validate($request, [
// 'description' => 'required'
//]);
$request->validate([
'description' => 'required'
]);
$task->description = $request->description;
$task->save();
return redirect('/dashboard');
}
}
}
10. Limpiar la caché de Laravel
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
Deja una respuesta
Lo siento, debes estar conectado para publicar un comentario.