Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
Require all granted
create first project
cd www
laravel new mysite
install nvm, npm and node.js as well as yarn **as root**:
export XDG_CONFIG_HOME="/opt"
apt install curl gnupg2
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$(curl -s https://api.github.com/repos/nvm-sh/nvm/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d ' ",')/install.sh | bash
echo 'export NVM_DIR="'$XDG_CONFIG_HOME'/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # This loads nvm bash_completion' > /etc/profile.d/nvm.sh
source /etc/profile.d/nvm.sh
nvm install node
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt update && apt install --no-install-recommends yarn
now **back to the regular user** to install bootstarp
cd ~/www/mysite
composer require laravel/ui
php artisan ui bootstrap
npm install
npm run dev
now comes something that we would not have had to do if we where running the laravel built-in dev server as unprivileged user.. we now run into some privilege issues because the web-server is running as ''www-data'' and I am using a different user to edit the files in ''/var/www''. All files are readable to the web-browser but they aren't writeable and that is a problem when it comes to laravel's log directory and bootstrap's cache directory, so we have to fix the permissions in there:
and fix the permissions (''storage'' and ''bootstrap/cache'' have to be writeable for the web-server)
sudo chgrp -R www-data mysite
sudo chmod -R ug+rwx mysite/storage mysite/bootstrap/cache
now adjust the ''DocumentRoot'' in your apache configs to point to ''/var/www/mysite/public''
systemctl reload apache2
===== Basics =====
Laravel follows the Model View Controller design principle.. where the View is basically your GUI, the Webpage in this case, the Model handles storing and reading your data.. so that's your database and some PHP scripts to access the database and retrieve Entries from it, and the Controller is the glue between the two, that's where your actual program logic happens.. the controller does something with the data and passes the result to the View.
**artisan** is a helper utility, it is a php cli script which is stored in the root of your Laravel project and on linux systems it is executable. so artisan can be called simply by typing ''./artisan'' in the root directory of the Laravel project. on ther systems one may need to run ''php artisan''.
**Blades** (stored in ''resources/views/*.blade.php'' by default) are templates for generating the Webpage, or the View. They have their own templating language. The simplest example is {{ $var }}
which in PHP would be something like '''' so it html encodes the contents of ''$var'' and prints it in-place where this expression is written. Blades also have some control structures and much more.
**Public folder** ''public/'' is the directory that is set up as your ''DocumetnRoot'' so that's the only directory that is visible to the outside. This means, that's where all your images and custom css go!
**Routes** (stored in ''routes/*.php'' by default) are needed to assign a URL to a View. look at the ''web.php'' file which contains an example route for a ''get'' request to ''/'' and forwards it to the ''weclome'' blade. the ''get'' from ''Route::get()'' actually means that it should handle a ''GET'' request.. so that's not a getter function for any type of route!. routes can also contain parameters which are passed as http parameters (like in ''index.php?parameter=abc'') using the function ''$myvar = request('parameter')'' or parameters can be passed as positional arguments in urls like ''/users/1234/comments/233'' where ''1234'' is the user-id and ''233'' is a comment id. this would be parsed using ''Route:get('/users/{uid}/comments/{cid}', function ( $myId,$commentId )....'' which passes whatever is behind ''users/'' in your url as the first argument and whatever is behind ''comments/'' as your second argument to your controller function. So the order of '{uid}' and '{cid}' is also the order in which the arguments are passed on to the controller function. The names of these placehoders don't matter for this purpose. they only come into play if you want to use some more advanced things like add a condition to the route using ''Route::get(..)->where('name','[A-Za-z]+');'' for example to filter out invalid URL's even before calling the controller.
**Layouts** (stored in ''resources/views/layouts/*.blade.php'' by best practice) basically a blade that contains common elements of multiple other blades is called a layout, like the http header and footer part.. a layout isn't simply included into the other blades.. instead, a blade for a view contains a ''@extends(layouts.mylayout)'' at the beining which would load the file ''resources/views/layouts/mylayout.blade.php''. also the main view template blade now needs to contain sections. a section named "content" would start with ''@section('content')'' and end with ''@endsection''. Now ''mylayout'' file can insert such a section at a given place by using the ''@yield('content')'' directive. so it's a little more complicated than a simple include, but also a lot more flexible as a result.
**Controllers** (stored in ''app/Http/Controllers/*.php'' by default) are, well, the controllers :) they are usually called by the route script and return an array of data to the blade that is called by the function. A new controller is created using ''artisan make:controller MyController'' (it is a naming convention, that the controller name should start with a captial letter). To call a function of the Controller, the route file ''web.php'' for example contains a line like ''Route::get('/users/{id}', 'UserController@show');'' which calles the show function in user controller and passes a argument ''$id=1234'' in the above example. the show function in the UserController typically then returns a view just like the ''Route::get()'' example in the ''web.php file''.
**database connections** are configured in ''.env'' in your project root directory. by default laravel is configured to use mysql on localhost. so just create a new database and a user for it and adjust the config in ''.env''
**migrations** (stored in ''database/migrations/*.php'') define database tables that laravel will create for us. so instead of manually adding new database tables to the database or adding fields to a table, we create or adjust migration php scripts. they have a ''up()'' and ''down()'' method. the ''up()'' method contains the definition of what is to be created when the migration is made, the ''donw()'' method contains the reverse action, so usually a drop command of some sorts. migrations are created by using ''artisan make:migration create_mytable_table'' where ''mytable'' is the name of my new table. this will crate a template migration. to then execute the migration and actually create the database tables you have to run ''artisan migrate''. to revert migrations, there are several options which can be seen by running ''artisan migrate:'' which lists all migrate commands. the most important during development is probably ''artisan migrate:fresh'' however, whenever a migration is rolled back, it essentially deletes the table, so that also deletes all the data in it. if you don't want to delete the data in the table and simply want to add one or more fields, you don't change the existing migration, instead you create a new migration for example ''artisan make:migration add_myfield_to_mytable_table'' the ''add .. to .. table'' portion is important.. artisan will understand tha and create a template for you which does exactly that, so you simply have to edit that file and add your field definition(s), then run ''artisan migrate'' and your field is added.
**eloquent models** (saved in ''app/Providers/*.php'' by default) contain the definition of the Model in our MVC. An eloquent model extends a class which provides functions like ''get()'', ''find()'' and such to get and manipulate database data. so through these eloquent models you access the database tables. new models can be created using artisan: ''artisan make:MyModel'' where ''MyModel'' is the name of the new eloquent model class to be created to access a certain table. The standard naming convention is that a table should be named in the plural of whatever it contains, i.e. "users" and the model should then be named in the singular form with a capital letter, i.e. "User". Laravel will by default take the Model's name and pluralize it (in english) and convert it to lower case to set the database name. this may not always lead to very readable code. therefore the table name can be overwritten by defining the protected variable ''protected $table='myTable'';' in the Model class definition for example. \\
To use a model in a controller we have to add ''use App\MyModel;'' at the beginning of our controller. we can then use ''$somevar=MyModel::all()'' to get all entries from our table for example. ''$somevar'' will be an array of objects in this case. To search for an entry we can use i.e. ''MyModel::where('field','value')->get()'' which will return all entries where the field ''field'' has the value ''value''.. notice how we needed to call the ''get()'' method of the returned object to get our array of objects of entries.
**processing post requests** first of all we need a form to post some data. we can use a normal html form, but we need to add a directive ''@csrf'' in the blade just after the form tag. this is to prevent cross site request forging. laravel will automatically take care of providing a token to verify a form from our webpage was used from a valid session, so nobody can to post some date by hijaking a user session of an authenticated user for example. To process the POST request we now need the ''post()'' method rather than the ''get()'' method in our route and the controller method that is being called by the route is usually called ''store()''. The Controller does not return a view, insead it returns a redirect to a view. this is probably to prevent reload issues. we can pass arguments to a redirect by using session variables. they can be added by using somethig like ''return redirect('{{ session('sessvar') }}
to output our value.
$table->foreignId('job_id')->constrained();
which will automatically define a foreing ID in the current table that is linked to the ''jobs'' (plural) table's ''id'' field. the ''constrained()'' method at the end is there to tell laravel to create a constraint in MySQL for this relationship.
so here is how various relationships are defined in the model for the respective table(s)
==== 1:1 relationships ====
here's an example.. the ''assessments'' table contains a field ''job_id'' defined like above, which links to a single job out of the ''jobs'' table:
in the **Assessment Model** we write:
public function job(){
return $this->belongsTo('App\Models\Job');
}
and vice versa, in the **Job Model** we write:
public function assessment(){
return $this->hasOne('App\Models\Job');
}
now we can simply access the job via the assessment by using something like ''$assessment->job->name'' and vice versa we can get a property from the assessment via the job like so ''$job->assessment->overview''
===== Naming Conventions =====
I'm going to use the examples of the above mentioned YouTube Series here, which is all about a pizza place :)
* Model Name: Pizza
* Table Name: pizzas
* Controller Name: PizzaController
* to return the main ''/pizzas'' view: ''index'' called from controller@method ''PizzaController@index'' in the route
* to return a single pizza entry from a url like ''/pizzas/{id}'': view ''show'' called from controller@method ''PizzaController@show'' in the route
* to create a new entry into the pizzas table: view ''create'' called from controller@method ''PizzaController@create'' in the route
* to process a POST request: **no view** (instead use a '' return redirect('
public function store(Request $request){
$validData = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
// anything from here on will only be executed if the above validation was successful
}
there are many [[https://laravel.com/docs/8.x/validation#available-validation-rules|validation rules]] available already in Laravel and custom rules can be added if needed.
also note, that the ''$validData'' variable now contains an array with the validated data in a format that is compatible with the ''create'' methods for the laravel models. So if you set up your model's ''$fillable'' property correctly and your form field names correspond to your database field names, you can pass this array straight to the ''create()'' method of your model and create a new database entry like that!
now here are a few cool things about the validator:
* if it finds an invalid field, it will stop right there and laravel will show you the form page again without any further routes needed.
* in addition to bringing the user back to the form, laravel will provide an ''$error'' variable which can be used in the balde
* even better, there is a ''@error(fieldname)'' blade syntax which will do whatever follows after it if the specified field is invalid
* you also get a ''old(fieldname)'' function which you can use to populate your input fields with the original inputs, so the user won't have to enter eveything again.. see the eample below.
here's an example part of a (bootstrap based) blade with a form that will show some info if the validation fails:
@if ($errors->any())
You did not fill in all the form fields correctly, please correct or complete the Information given below
@endif