Laravel (PHP)
- Install php & php-xml
- 1- Installing Node and npm through nvm
- 2- install composer (package manager) in current directory
- 3- create Laravel project
- 4- This example uses MySQL
- 5- Configure your project’s database settings:
- 6- Let’s setup the model, migration and controller:
- 7- Migration:
- 8- Model
- 9- Controller:
- 10- Web Routes:
- 11- Views:
- 12- Miscellaneous:
Install php & php-xml
Option 1
sudo apt update
sudo apt install php
sudo apt install php-xml
php -v #Check version with
php -ini | grep php.ini #Check config file with
/etc/php/8.1/cli/php.ini #Config file location
#Run server: go to project's directory and do:
php -S localhost:8000
Option 2
Install XAMPP
/opt/lampp/etc/php.ini #Config file location
#Run server:
sudo /opt/lampp/lampp start
#turn it off:
sudo /opt/lampp/lampp stop
Setup config file php.ini
#Un-comment following extensions:
extension=curl
extension=fileinfo
extension=gd
extension=mysqli
extension=openssl
#Enable those PDO you'll be using.
extension=pdo_firebird
extension=pdo_mysql
extension=pdo_oci
extension=pdo_odbc
extension=pdo_pgsql
extension=pdo_sqlite
1- Installing Node and npm through nvm
Install nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
nvm install 18
nvm use 18
2- install composer (package manager) in current directory
Go here
…and follow CLI instructions.
You should now have a “composer.phar” on current directory.
#Add it to ~/bin/ to make it accessible from anywhere
mv composer.phar ~/bin/composer
3- create Laravel project
php composer.phar create-project laravel/laravel example-app
or, if you did “mv composer.phar ~/bin/composer”, then:
composer create-project laravel/laravel example-app
After project has been created, start Laravel’s local development server:
cd example-app
php artisan serve #or ./artisan serve
4- This example uses MySQL
For most use-cases, an empty database & a single user with enough privileges will be enough
sudo apt install mysql-server
sudo mysql_secure_installation
systemctl status mysql
sudo mysql
CREATE DATABASE databasename;
CREATE USER 'username'@'localhost' IDENTIFIED BY 'enterPasswordHere';
GRANT CREATE, DROP, ALTER, INSERT, SELECT, UPDATE, DELETE ON databasename.* to 'username'@'localhost';
New user can now access MySQL like this:
mysql -u username -p
USE DATABASE databasename;
Or, with:
mysql -h localhost -u username -p databasename
5- Configure your project’s database settings:
Edit your example-app/.env file accordingly.
#configure database:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=databasename
DB_USERNAME=username
DB_PASSWORD=PaSSword*
6- Let’s setup the model, migration and controller:
./artisan make:model professor -mc
This will automatically create:
—>Model [app/Models/professor.php] Each model class defines a mapping between the database table and the application code. This mapping is based on naming conventions. i.e.: ‘professor’ model maps to a ‘professors’ database table.
—>Migration [database/migrations/TSTAMP_create_professors_table.php] Migrations help you create new tables in your database or change existing ones. Each migration file contains instructions on what the table should look like. Like a database ‘blueprint’ or ‘cookbook’.
—>Controller [app/Http/Controllers/ProfessorController.php] Controllers organize your application’s logic into reusable and maintainable classes. i.e.: rendering views or returning JSON data.
7- Migration:
Go to the [database/migrations/TSTAMP_create_professors_table.php] Edit the “up()” function to include the columns you want:
public function up(): void
{
Schema::create('professors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('age');
$table->timestamps(); /*if you don't want this column, pay attention to next step!*/
});
}
If database doesn’t yet contain ‘professors’ table, this command below will create it:
./artisan migrate
#or this one below to reset whole table and start over:
./artisan migrate:fresh
8- Model
Go to the [app/Models/professor.php] Right below where it says “use HasFactory”, add:
class professor extends Model
{
use HasFactory;
public $timestamps = false; /*delete this line if you're okay including timestamps!*/
protected $table = 'professors';
protected $fillable =[
'name'.
'age'
];
}
9- Controller:
Go to the [app/Http/Controllers/ProfessorController.php]
use App\Models\professor; #Import professor model!
class ProfessorController extends Controller
{
/* RETRIEVE
| Query database, look for all professors.
| Assign all entries to a list $teachers.
| Pass this data from controller to view
| through template variable 'roster'.
| View rendered: 'resources/views/allOfThem.blade.php'.
*/
public function retrieve(){
$teachers = professor::all();
return view('allOfThem', ['roster' => $teachers]);
}
/* CREATE
| Render form: 'resources/views/newProf.blade.php'
|
| $request receives form-data as JSON.
| Map the value of each key to a property
| of a professor instance.
| Pass this object from controller to view
| through template variable 'prof'.
| Once database is updated, this view is rendered:
| 'resources/views/formSubmitted.blade.php'.
*/
public function start_create(){
return view('newProf');
}
public function commit_create(Request $request){
$entry = new professor;
$entry->name = $request->input('name');
$entry->age = $request->input('age');
$entry->save();
return view('formSubmitted', ['prof'=>$entry]);
}
/* UPDATE
| Locate the professor that matches the $identifier.
| Pass this data from controller to view through
| template variable 'old'. Rendered view will be this
| form: 'resources/views/updateProf.blade.php'
|
| $request receives form-data as JSON.
| Map the value of each key to a property of an
| already existing database entry. Pass that object from
| controller to view through template variable 'prof'.
| Render view: 'resources/views/formSubmitted.blade.php'
*/
public function start_update($identifier){
$entry = professor::find($identifier);
return view('updateProf', ['old' => $entry]);
}
public function commit_update(Request $request, $identifier){
$entry = professor::find($identifier);
$entry->name = $request->input('name');
$entry->age = $request->input('age');
$entry->save();
return view('formSubmitted', ['prof' => $entry]);
}
/* DELETE
| Locate the professor that matches the $identifier.
| Pass this data from controller to view through
| template variable 'old'. Rendered view will be this
| form: 'resources/views/deleteProf.blade.php'
|
| Upon form-submission, selected professor is
| deleted from database. View rendered is:
| 'resources/views/allOfThem.blade.php'
| and its template variable 'roster' is mapped
| to a list of all database entries.
*/
public function start_delete($identifier){
$entry = professor::find($identifier);
return view('deleteProf', ['old' => $entry]);
}
public function commit_delete($identifier){
$entry = professor::find($identifier);
$entry->delete();
return view('allOfThem', ['roster'=>professor::all()]);
}
}
10- Web Routes:
Web routes are typically used for handling views. We’ll now map routes to controller functions.
Go to routes/web.php
use App\Http\Controllers\ProfessorController; #Import controller!
#GET request. Endpoint 'home' maps method 'retrieve' to URL '/homepage'.
Route::get('/homepage', [ProfessorController::class, 'retrieve'])->name('home');
#GET request (renders a form). Endpoint 'add' maps method 'start_create' to URL '/newOne' .
Route::get('/newOne', [ProfessorController::class, 'start_create'])->name('add');
#POST request. Endpoint 'adding' maps method 'commit_create' to URL '/newEntry'.
Route::post('/newEntry', [ProfessorController::class, 'commit_create'])->name('adding');
/*GET request (renders a form). Endpoint 'update' maps method 'start_update'
to URL '/update_item/' with route parameter 'id'*/
Route::get('/update_item/{id}', [ProfessorController::class, 'start_update'])->name('update');
/*PUT request. Endpoint 'updating' maps method 'commit_update'
to URL '/update_submit/' with route parameter 'id'*/
Route::put('/update_submit/{id}', [ProfessorController::class, 'commit_update'])->name('updating');
/*GET request (renders a form). Endpoint 'delete' maps method 'start_delete'
to URL '/delete_item/' with route parameter 'id'*/
Route::get('/delete_item/{id}', [ProfessorController::class, 'start_delete']);
/*DELETE request. Endpoint 'deleting' maps method 'commit_delete'
to URL '/delete_submit/' with route parameter 'id'*/
Route::delete('/delete_submit/{id}', [ProfessorController::class, 'commit_delete'])->name('deleting');
11- Views:
#Create this directory:
mkdir public/css/
#Create and edit your css file:
touch public/css/app.css
Make ‘layouts’ to recycle style across child-templates:
mkdir resources/views/layouts/
touch resources/views/layouts/parent.blade.php
Yielded sections are placeholders for child-templates to fill-in.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('pagename')</title>
<!--Pay attention! This is how you link the css file!-->
<link href="" rel="stylesheet">
</head>
<body>
@yield('contents')
</body>
</html>
Go to resources/views/ and create these blade templates:
—>[resources/views/allOfThem.blade.php]
@extends('layouts.parent')
@section('pagename')
Homepage
@endsection
@section('contents')
<!--Pay attention! see how route-parameters are passed to endpoints! -->
<table>
@foreach($roster as $member)
<tr>
<td></td>
<td></td>
<td><a href="">edit</a></td>
<td><a href="">delete</a></td>
</tr>
@endforeach
</table>
<a href="">Add new professor!</a>
@endsection
—>[resources/views/newProf.blade.php]
@extends('layouts.parent')
@section('pagename')
...Adding
@endsection
@section('contents')
<form method="POST" action="">
@csrf <!-- Cross-Site Request Forgery token -->
Known as: <input type="text" name="name" required> <br>
Winters: <input type="number" name="age" required> <br>
<button type="submit">Add new entry</button>
</form>
<a href="">Cancel</a>
@endsection
—>[resources/views/updateProf.blade.php]
@extends('layouts.parent')
@section('pagename')
...Updating
@endsection
@section('contents')
<form method="POST" action="">
@csrf <!-- Cross-Site Request Forgery token -->
@method('PUT') <!-- Specify that it's a PUT request -->
A.K.A: <input type="text" name="name" value="" required> <br>
Summers: <input type="number" name="age" value="" required> <br>
<button type="submit">Submit changes</button>
</form>
<a href="">Cancel</a>
@endsection
—>[resources/views/deleteProf.blade.php]
@extends('layouts.parent')
@section('pagename')
...Deleting
@endsection
@section('contents')
Do you wanna permanently delete ""?
<form method="POST" action="">
@csrf <!-- CSRF token -->
@method('DELETE') <!-- Specify that it's a DELETE request -->
<button type="submit">Yes</button>
</form>
<a href="">Cancel</a>
@endsection
—>[resources/views/formSubmitted.blade.php]
@extends('layouts.parent')
@section('pagename')
Success!
@endsection
@section('contents')
(age: ) added<br>
<a href="">back to homepage</a>
@endsection
12- Miscellaneous:
Common Paths
example-app/public/css #i.e.: CSS files
example-app/resources/views/ #i.e.: Blade templates and layouts
example-app/app/Models #i.e.: Models
example-app/app/Http/Controllers/ #i.e.: ModelControllers
example-app/routes/web.php #i.e.: MVC controller
Authentication scaffolding
Be careful! This will overwrite your routes/web.php file. ‘Breeze’ package protects your endpoits with user-authentication:
composer require laravel/breeze --dev
./artisan breeze:install #Blade with Alpine/ Darkmode/ PHPUnit
./artisan migrate
npm run dev #Activates authentication service. Run on different terminal
Add your endpoints under middleware(‘auth’)->group(function(){…}):
use App\Http\Controllers\ProfessorController; #Import controller!
#{...}
Route::middleware('auth')->group(function () {
#These were auto-generated
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
#Here you can put the endpoints previously defined on step 10.
#Important: Any endpoint left out of this scope remains accessible without authentication.
});
Generating PDF reports
composer require barryvdh/laravel-dompdf
Edit config/app.php Look for this comment:
/*
* Application Service Providers...
*/
Add this one to the list of ‘providers’:
Barryvdh\DomPDF\ServiceProvider::class,
Time to update the app/Http/Controllers/ProfessorController.php:
use PDF #Dont forget this import!
public function getPDF(){
$teachers=professor::all();
$pdf = PDF::loadView('allOfThem', ['roster'=>$teachers]);
return $pdf->stream('rosterInPDF.pdf');
#return $pdf->download('downloadedRoster.pdf');
}
Finally, map it to a route at: routes/web.php
/*GET request (renders a view as a PDF). Endpoint 'pdf'
Maps method 'getPDF' to URL '/generatePDF'.*/
Route::get('/generatePDF', [ProfessorController::class, 'getPDF'])->name('pdf');
Word of caution!. PDF rendering can be very slow if CSS used in the view is from: public/css/
i.e.: this would take a full minute to render!
<link href="" rel="stylesheet">
Therefore, a <style> tag or in-line CSS is preferred!