Laravel View, Forms, Input, Validation, Authentication Layer Web Technologies II Darja Solodovnikova Adopted from Artūrs Lavrenovs
View Visual representation of a model Job is simple - take variable and output it inside HTML (or other markup) Default approach (for most frameworks) HTML with PHP snippets echoing variables Laravel has Default View engine (most PHP MVC frameworks also have such) Blade templating engine
Laravel Default View Goal of the most browser requests is to receive a rendered view You have to return the result from the Controller Code to call View rendering View::make($viewname, $data); Where View is Laravel view class and make is method rendering the view $viewname is the name of the view, actual view is stored as $viewname.php in resources/views/ $data is associative array containing data to display which gets expanded inside a view Alternative view($viewname, $data);
Laravel Simple View File HTML file containing PHP code for only printing variables received only from $data Code inside app/http/routes/web.php Route::get('/post/{action}/{post_id?}', function($action, $post_id = "default") { $data = array('action'=>$action,'postid'=>$post_id); return view('simple', $data); }); Code inside resources/views/simple.php <!doctype html> <html lang="en"> <head><meta charset="utf-8"> <title><?php echo $action;?></title> </head> <body><p><?php echo $postid;?></p></body> </html>
Blade Templating Engine https://laravel.com/docs/master/blade
Why Blade? Default View approach still has some ugliness inside HTML template PHP tags <?php?> echo and ; Additional processing of output htmlspecialchars() or htmlentities() Loops with { } Blade replaces most of the PHP ugliness with its own cleaner syntax But does not restrict you from using plain PHP Views are compiled into plain PHP code and cached until they are modified zero overhead
How to Use Blade Put Blade templates in resources/views But there is a difference in naming, Blade templates should be named $viewname.blade.php Calling the view is the same View::make($viewname, $data); or view($viewname, $data);
Blade Syntax Outputting Variables To output the variable, use {{ $variable }} It automatically converts HTML entities and acts as a defense against XSS If you do not need escaping and you are sure data is not from user input then you can use {!! $variable!!} If you aren't sure if the variable has been set {{ $variable or 'Default' }} Other special Blade things are prefixed with @
Blade Control Structures Branching @if ($color == 'white') <p>good choice</p> @elseif ($color == 'black') <p>classic choice</p> @else <p>strange choice</p> @endif @unless ($color == 'white') <p>color is not white</p> @endunless
Blade Control Structures Loops @foreach ($names as $name) <p>{{ $name }}</p> @endforeach @for ($i = 0; $i < 99; $i++) <p>{{ $i }}th number</p> @endfor @while (false) <p>should not see this</p> @endwhile
Blade Control Structures Loop Variable $loop variable is available inside the loop @foreach ($conferences as $conf) @if ($loop->first) This is the first conference. @endif @if ($loop->last) This is the last conference. @endif <p>this is a conference {{ $conf->name }}</p> @endforeach Other properties $loop->index, $loop->iteration, $loop->remaining, $loop->count $loop->depth, $loop->parent
Blade Templating All of the above was just a syntactic sugar Blade best features are templating which allows to get rid of the majority HTML in view files Separating data output from HTML These features are Inclusion join multiple separate views Inheritance inherit view skeleton Sectioning separating HTML from data output
Blade Parent Template (master.blade.php) <html> <head> <title>@yield('title')</title> </head> <body> @include('sidebar') <div class="container"> @yield('content') </div> </body> </html>
@extends('master') Blade Child Template without HTML @section('title', 'Conferences') @section('content') @for ($i = 0; $i < count($conferences); $i++) <p>conference {{$i}} name: {{$conferences[$i]->name}}</p> @endfor @endsection
Using Blade for Elements of a Collection @extends('master') @section('title', 'Conferences') @section('content') @each('confinfo', $conferences, 'conference') @endsection confinfo.blade.php <p>name: {{$conference->name}}</p> <p>description: {{$conference->description}}</p>
URL https://laravel.com/docs/master/helpers#method-url Laravel offers helper functions to deal with URL Generate full URL - includes domain and protocol Eliminates mistakes using absolute/relative paths Usable inside Views {{ }} and other code Code url('login') will generate for our project http://project.dev/login Similarly asset() allows to generate link to static content in case you move your static files to other place (like CDN) If you use route() and action() inside your Views Actual URL will change automatically These functions maps to routes and controllers not fixed path
URL Redirection Action of telling user browser to go to a different URL, handled by HTTP 3xx codes Usually you want redirect functionality in your web application For login, logout, registration Laravel code for redirects return redirect('/'); return redirect()->route('hotel.index'); return redirect()->action('hotelcontroller@index');
Laravel Collective Forms https://laravelcollective.com/docs/5.4/html
Forms Creating forms in HTML can be gruesome task Creating forms with HTML and PHP (populating data) even gruesomer task It is possible to use special syntax to define and populate your forms in the View using Form class Generated Form has CSRF token For advanced functionality (e.g., Form autofilling from Model, resetting, custom buttons) review documentation
Laravel Collective Forms Older versions of Laravel supported forms (Illuminate/Html/FormBuilder) Laravel 5 does not support Forms out of the box Forms are maintained by Laravel Collective Installation composer require laravelcollective/html In config/app.php add new provider Collective\Html\HtmlServiceProvider::class, to the providers array In config/app.php add new class aliases to the aliases array: 'Form' => Collective\Html\FormFacade::class, 'Html' => Collective\Html\HtmlFacade::class,
Creating Form Form is just another View, create View and use it from Controller or route You need to open the Form inside the View, you can use static url, routes, controllers as target {!! Form::open(array('url' => 'login'))!!} //Put your form fields here! {!! Form::close()!!} To imitate PUT and DELETE methods add a hidden field _method to the form {!! Form::open(array('url' => 'login', 'method' => 'put'))!!}
Creating Form fields To create field description (label) bound by name {!! Form::label('email', 'E-Mail Address')!!} After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well. To create text field (most common type of field), argument is name {!! Form::text('email')!!} To create other type of fields use other Form methods textarea(), password(), checkbox(), radio(), select(), file(), hidden(), etc.
Filling Form Fields with Default Values You can fill form fields with default values or data from the database Depending on the field type, the default value can be after the name as the 2nd argument {!! Form::text('email', 'darja@example.com')!!} Some types of fields allow selecting/checking them by using the 3rd argument {!! Form::checkbox('receive_email', 'yes', true)!!}
Submitting Form You can submit forms by pressing Enter Usually users expect some button to be pressed to submit forms In Laravel Form you can add submission button {{ Form::submit('login') }}
Form Model Binding You can populate form elements with data from the model automatically {!! Form::model($conference, ['method' => 'PUT', 'url'=> ['conference.update', $conference->id]])!!} Now all form elements will be filled with the model's value matching the field's name. However, if there is an item in the Session flash data matching the input name, that will take precedence over the model's value. The priority looks like this: Session flash data (old Input) Explicitly Passed Value default values Model attribute data
Model Form Example {!! Form::model($conference, ['method' => 'PUT', 'url'=> ['conference.update', $conference->id]])!!} <div> {!! Form::label('name', 'Name')!!} {!! Form::text('name')!!} </div><div> {!! Form::label('acronym', 'Acronym')!!} {!! Form::textarea('acronym')!!} </div><div> {!! Form::label('rating', 'Rating')!!} {!! Form::number('rating')!!} </div> {!! Form::submit('Save')!!} {!! Form::close()!!}
Input https://laravel.com/docs/master/requests#retrieving-input
User Input $_GET is not the recommended way of handling user input in Laravel Path parameters using routes or Controller mapping allow a way for accessing simple/short data from the users, also use for only reading data Complicated input and also data modification (CUD from CRUD) should be handled by POST requests default for Laravel Collective Forms from the previous chapter
Laravel Input We already reviewed how to access Path parameters in routes and Controllers POST (and GET) input is accessed by Input class But there are also other often forgotten methods of user input COOKIES SESSION REQUEST itself
Input Class - input() Helper Function We will use it to handle POST data but can be used also for GET You can get all the input with $request->all(); or single variable $request->input('name'); or $request->name; Default value $request->input('name', 'Default Name'); Can get multiple variables $request-> only('username', 'password'); or remove some $request->except('email'); Check if variable is provided $request->has('name') or is filled $request->filled('name')
input()helper Example public function update(request $request, $id) { $city = City::findOrFail($id); $city->fill($request->all()); $city->save(); return redirect('city'); } In City.php protected $guarded = ['id', 'created_at', 'updated_at'];
Cookie Class - cookie() helper function Cookies allow to store some (small) data in the clients browsers Laravel Cookies are signed and encrypted users don't see Cookie value only name when accessing Cookies, Laravel will check if they have been tampered with Still you should not completely rely of the data being good, e.g., user can remove a cookie or put a different cookie Cookies should be used for something Not important, e.g., template choice and not storing user rights Not associated with User Model, e.g., template choice for unregistered user Old school: Cookies were used to communicate between PHP and JavaScript, now should use AJAX
Using Cookie Cookies may be attached to a Illuminate\Http\Response instance For specified time span, where 3rd argument is minutes $response->withcookie(cookie('color', 'blue', 10)); Forever (technically 5 years) $response->withcookie(cookie()-> forever('color', 'blue')); Cookies are read by $request->cookie('color');
Using Cookie with a view $response = new Response(view('welcome')); $response->withcookie(cookie('referrer', $request->referrer, 45000)); return $response; public function index() { Cookie::queue('visible', true, 15); return view('dashboard'); }
Session Class - flash() helper functions Session is the way to store data associated to the user on the server (opposite to cookie) As a result, you can put there all the important data and user specific data, you can trust this data Using session, Laravel allows you to keep input from one request during the next request. This feature is particularly useful for re-populating forms after detecting validation errors. Session is created for ALL requests (not only logged in users) by creating cookie laravel_session with unique id This id is used to map between browser and session data Session limitations By default session lives short time (in Laravel 2h) Session storage (FS/DB/RAM) can cause performance issues
Using Session Session works as key value store, same as cookie Creating new session field $request->session()->put('color', 'blue'); Using array as the value $request->session()->push('colors', 'blue'); $request->session()->push('colors', 'red'); Flashing input to the session All $request->flash(); Part of input $request->flashonly('title', 'body'); $request->flashexcept('password');
Using Session II Retrieving session data By key $request->session()->get('color'); All $request->session()->all(); Old data $request->old('name'); Deleting session data Retrieving and then deleting $request-> session()->pull('color'); Single $request->session()-> forget('color'); or all $request->session()->flush();
Request Class You can access data associated with a request Get Request object in the controller method public function update(request $request, $id) Methods to retrieve data from a request URI $request->path(); and $request-> is('admin/*'); full URL $request->url(); Method $request->method(); and $request->ismethod('post') Headers $request->header('useragent'); Server variable $request-> server('script_filename'); Be careful this data could be affected by the user
Validation https://laravel.com/docs/master/validation
Input Validation User input data is the evil that causes the most problems Checking user input in Vanilla PHP is gruesome and error prone task Laravel filters 2 biggest input problems SQL and XSS injections well automatically (if you don't make errors) Still you should filter data to match your business logic Such filtering in Laravel is done using Validation class
Simple Approach to Validation $this->validate($request, $rules); If the validation passes the execution will continue. If the validation fails the user will be redirected to his/her previous location. Validation errors will automatically be flashed to the session. You can access validation errors in $errors variable.
Validator Class To validate input data $validator = Validator::make($data, $rules); Where $data is user input, e.g., Input::all() or $request Where $rules is an associative array where Keys are the name of Form input field Values are the validation rules After validation you can Determine result: $validator->passes() or $validator->fails() Get error messages: $validator->messages() Validation is complex topic so we will review example
Validation Rules for a Form $rules = $rules = array( 'name' => 'required min:3 max:250', 'description' => 'required min:3', 'city' => 'required exists:cities,id', 'rating' => 'required integer min:0 max:5', 'price' => 'required numeric', 'logo' => 'required image mimes:jpeg', ); $this->validate($request, $rules); More validation rules are available in the documentation https://laravel.com/docs/master/validation#available-validation-rules
Error Messages in the Form @if (count($errors) > 0) <div> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif
Error Messages for Each Field {!! Form::open(['action' => 'ConfController@store'])!!} <div> {!! Form::label('name', 'Conference name')!!} {!! Form::text('name')!!} @if ($errors->has('name')) <span class="help-block"> <strong>{{ $errors->first('name') }}</strong> </span> @endif </div><div> {!! Form::label('description', 'Description')!!} {!! Form::textArea('description')!!} @foreach($errors->get('description') as $message) <span class="help-block"> <strong>{{ $message }}</strong> </span> @endforeach </div> {!! Form::submit('Create')!!} {!! Form::close()!!}
For More Complex Tasks Create a form request php artisan make:request UpdateCountryRequest Define validation rules public function rules() { return ['name' => 'required alpha_dash min:5 max:20 unique:countries,name,'.$this->route()-> getparameter('country')]; } Type-hint the request on your controller method public function update(updatecountryrequest $request, $id) You can also check, whether a user has rights to update the country in the method authorize()
Validation Summary Laravel simplifies working with input data There are a lot of validation rules available If the validation does not pass the user is redirected back Error display Validation errors are automatically included into session $errors variable is available in all the Views In the View Forms you can access and display error for each field Generated errors are human readable and directly usable (if necessary, you can define your own messages https://laravel.com/docs/master/validation#custom-error-messages)
Laravel Authentication Layer https://laravel.com/docs/master/authentication
Authentication User authentication is a common functionality of a generic web application Laravel provides authentication layer built on top of the User Model It is meant for you to integrate authentication layer in your own code, e.g., adding middleware We will review only the basics of authentication
Authentication out of the Box Auth\RegisterController handles new user registration. Auth\LoginController handles authenticating users for the application Auth\PasswordController contains the logic to help existing users reset their forgotten passwords. Authenticate middleware (accessed as auth) is used to allow only authenticated users to access a given route. Model User with automatic password encryption
RegisterController LoginController Methods for handling logging and registering If login/registering was unsuccessful by default the user is redirected to the login page and $errors variable is populated with login error If login/registering was successful by default the user is redirected to the home page (/home).
Adding Authentication Run php artisan make:auth on fresh application, which creates layout and views for login, registering (resources/views/auth) routes for all authentication end-points HomeController to handle post-login requests Optionally in the RegisterController and LoginController define The path for successful authentication protected $redirectpath = '/conf';
Checking if User is Logged in To check if user is logged in (previous slide) if (Auth::check()) { // You should use this to change // parts of the web page // depending of login status } Laravel uses User Model so when user is logged in you can access User fields $email = Auth::user()->email; $user_id = Auth::id;
Example Usage in RegistrationUpdateRequest public function authorize() { if (Auth::check()) { $regid = $this->route('registration'); return Registration::where('id', $regid)-> where('user_id', Auth::id())-> exists(); } return false; }
Checking if User is Logged in II But if you want to limit access to the whole Routes or Controllers you should use Middleware Route::get('profile', ['middleware' => 'auth', function() { }]); // Only authenticated users may enter... public function construct() { } $this->middleware('auth', ['except' => 'index']);
We Have Finished Laravel We have reviewed only the Laravel basics required to build simple generic web application Most of the reviewed functionality can be combined or utilized in more efficient manner We have skipped A LOT of useful stuff We have not touched any of the advanced stuff Now you have to study Laravel documentation on Your own https://laravel.com/docs/master