Blade Pattern

The last important piece of this puzzle is the blade template, which will contain all the HTML, CSS, and javascript for our simple application. Here’s the code – I’ll explain later.

<body>
   <h1> Upload a file </h1>
   <form id = "uploadForm" name = "uploadForm" action = "{{route ('upload')}}" enctype = "multipart / form-data">
       @csrf
       <label for = "fileName"> File Name: </label>
       <input type = "text" name = "fileName" id = "fileName" required /> <br />
       <label for = "userFile"> Select a File </label>
       <input type = "file" name = "userFile" id = "userFile" required />
       <button type = "submit" name = "submit"> Submit </button>
   </form>
   <h2 id = "success" style = "color: green; display: none"> Successfully uploaded file </h2>
   <h2 id = "error" style = "color: red; display: none"> Error Submitting File </h2>
   <script src = "// ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"> </script>
   <script>
        $ ('# uploadForm'). on ('submit', function (e) {
            e.preventDefault ();
            var form = $ (this);
            var url = form.attr ('action');
            $ .ajax ({
                url: url,
                type: "POST",
                data: new FormData (this),
                processData: false,
                contentType: false,
                dataType: "JSON",
                success: function (data) {
                    $ ("# fileName"). val ("");
                    $ ("# userFile"). val ("");
                }
            }). done (function () {
                $ ('# success'). css ('display', 'block');
                window.setTimeout (() => ($ ("# success"). css ('display', 'none')), 5000);
            }). fail (function () {
                $ ('# error'). css ('display', 'block');
                window.setTimeout (() => ($ ("# error"). css ('display', 'none')), 5000);
            });
        });
   </script>
</body>
</html>

This is what our download page looks like :

This is a very typical example of a blade file containing an HTML form and javascript / jQuery to add asynchronous functionality (so the page doesn’t refresh). There is a basic <form> tag with no method attribute (which I’ll explain in a second) and a curious action attribute with the value {{route (‘file.upload’)}}. In the blade, this is what is known as a directive. The directive is just a fancy name for a function – these are functions specific to blade templates that perform various operations that are common to building web pages and web applications. For a better understanding of all the cool shit a blade can do, check out the docs here . In the above case, we are using the route directive to generate the URL for submitting the form.

Remember that we defined our routes earlier in the application inside the web.php file, providing an easy-to-remember name for each one. The {{route ()}} directive takes the name of a route, looks for it in the list of internal cached routes and generates a full URL based on the definition of that route in the web.php file. In this first case, we are specifying that we want the form to submit its submitted data to our application’s / process URL, which is defined as a POST route.

The next weird thing you might have noticed is the @csrf tag just below the opening form tag. In the blade, this tag generates a _token parameter on the form, which is validated internally by the application before the form data is allowed to be processed. This ensures that the data inside the form is of valid origin and prevents cross-site-request-spoofing attacks. For more information on this, see the docs .

After that we define our form as normal, however, note that the names of our form parameters, userFile and fileName, are exactly the same as defined inside our request object. If we forgot to include the input for a given parameter that was defined in the request object (or wrote it incorrectly), the request would fail and return an error preventing the original form request from reaching the controller method located at UploadController @ process.

Try it and submit some files to your application using this form. After that go to the / list page to see the contents of the download folder, and the files you downloaded are listed in the table:

Big picture

Let’s take a step back and see what we’ve done.

This diagram depicts the application as it is now (high-level details excluded):

You must remember that the request object we created at the beginning of this lesson must have the same parameters defined in its rules method as in the form in the blade template (unless you reread the section on Creating validation logic). The user enters a form on a web page that is rendered using the blades templating engine (this process is of course done on autopilot so we don’t even need to think about it) and submits the form. The jQuery template code at the bottom stops the default submission (which automatically redirects to a separate page), creates an ajax request, loads the request with the form data and uploads the file and sends it all to the first layer of our Application: request.

The request object is populated by binding parameters within the rules () method to the submitted form parameters, and then validating the data against each specified rule. If all the rules are satisfied, the request is passed to any controller method that matches the values ​​defined in the web.php route file. In this case, it’s the process () method of UploadController that does the work. After we get into the controller, we already know that the request passed validation, so we don’t need to re-check if the given filename is actually a string or a userFile parameter, actually contains some type of file … We can continue as usually.

The controller method then extracts the validated parameters from the request object, generates the fully qualified filename by concatenating the passed fileName parameter with the original userFile extension, stores the file in a directory in our application, then returns a simple JSON-encoded response confirming that the request was successful. The answer comes from jQuery logic, which performs a few more UI related tasks such as displaying a success (or error) message for 5 seconds, then hiding it, and clearing previous form entries … this is so User knows for sure that the request is was successful, and can download another file if desired.

Also notice the diagram above where the line goes between client and server. This concept is absolutely essential to understand and will help you solve problems and problems that may arise in the future when juggling, such as multiple asynchronous requests that may arise at any given time. The split happens right at the border of the request object. The request object itself can be thought of as a “gateway” to the rest of the application … It does the initial validation and registration of form values ​​submitted from the web browser. If they are considered valid, then this continues to the controller. Everything before that is at the forefront (“client” literally means “on the user’s computer”). The response is returned from the application back to the client side,

Controller modification

Open the app / Http / Controllers / UploadController and make the following changes to it:

<? php 
namespace App \ Http \ Controllers; 
use Illuminate \ Contracts \ Container \ BindingResolutionException; 
use Illuminate \ Http \ Request; use App \ Http \ Requests \ UploadFileRequest; // our new request class use Illuminate \ Support \ Facades \ Storage; class UploadController extends Controller { / ** * This is the method that will simply list all the files uploaded by name and provide a * link to each one so they may be downloaded * * @param $ request: A standard form request object * @return \ Illuminate \ Contracts \ View \ Factory | \ Illuminate \ View \ View * @throws BindingResolutionException * / public function list (Request $ request) { $ uploads = Storage :: allFiles ('uploads'); return view ('list', ['files' => $ uploads]); } / ** * @param $ file * @return \ Symfony \ Component \ HttpFoundation \ BinaryFileResponse * @throws BindingResolutionException * / public function download ($ file) { return response () -> download (storage_path ('app /'.$ file)); } / ** * @return \ Illuminate \ Contracts \ View \ Factory | \ Illuminate \ View \ View * @throws BindingResolutionException * / public function upload () { return view ('upload'); } / ** * This method will handle the file uploads. Notice that the parameter's typehint * is the exact request class we generated in the last step. There is a reason for this! * * @param $ request: The special form request for our upload application * @return array | \ Illuminate \ Http \ UploadedFile | \ Illuminate \ Http \ UploadedFile [] | null * @throws BindingResolutionException * / public function store (UploadFileRequest $ request) { // At this point, the parameters passed into the $ request (from form) are // valid - they satisfy each of the conditions inside the rules () method $ filename = $ request-> fileName; // parameters have already been validated $ file = $ request-> file ('userFile'); // that we don't need any additional isset () $ extension = $ file-> getClientOriginalExtension (); // grab the file extension $ saveAs = $ filename. "." ... $ extension; // filename to save file under $ file-> storeAs ('uploads', $ saveAs, 'local'); // save the file to local folder return response () -> json (['success' => true]); // return a success message } }

So this is a fairly simple approach to saving downloaded files to disk. Here’s a breakdown of the upload () method above:

  • Request class hint in controller method that acts as “meat and potato” so that we can automatically validate incoming data
  • Fetch the file from the (now checked out) request object inside the controller method (in this case we called it upload (), but it could also be called a more standardized name like store ()).
  • Grab the filename from the request
  • Generate a final filename that will be used to save the file. The getClientOriginalExtension () method simply gets the original extension of the uploaded file.
  • Save the file to the local filesystem using the storeAs () method, passing the named path in the / storage directory as the 1st argument and the filename to store as the second.
  • Return a JSON response indicating the request was successful

Creating validation logic

Let’s modify the request stub to suit the needs of our application. Modify the file so that it looks like this:

<? php

namespace App \ Http \ Requests;

use Illuminate \ Foundation \ Http \ FormRequest;

class UploadFileRequest extends FormRequest
{
   / **
    * Determine if the user is authorized to make this request.
    *
    * @return bool
    * /
   public function authorize ()
   {
       return true;
   }

   / **
    * Get the validation rules that apply to the request.
    *
    * @return array
    * /
   public function rules ()
   {
       return [
           'fileName' => 'required | string',
           'userFile' => 'required | file'
       ];
   }
}

Not a lot of changes, but note that the authorize () method now returns true instead of false. This method decides whether or not to allow the request to log into the application. If this parameter is set to false, the request will not log in (which is usually a method on the controller). This would be a very convenient place to do any checks for user authorization or any other logic that might decide if a request can move to a controller. For now, we just return true so that everyone and everything can use the query.

The other method, rules (), is where all the magic comes into play regarding validation. The idea is simple: return an array containing a set of rules as:

'formFieldName' => 'constraints this field has separated by pipe characters (|)'

There are many different check constraints that Laravel supports out of the box. For a complete list of these, check out the online documentation here . For our application, there will be two fields for loading, which are sent via a POST request from the form in the front-end. The fileName parameter must be included in the body of the form (that is, required) and used as the name of the file under which we will store the file in the storage (this is done in the controller – we will return to it a little later). We also specify that the filename should be a string by adding a pipe character (|) and the word “string”. Constraints are always limited to pipes, which allows you to specify any additional criteria for a given field on one line! What a power!

The second parameter, userFile, is the actual file that the user uploads from a form on a web page. UserFile is also required and must be a file. Note. If we expected the uploaded file to be an image, we would instead use an image constraint, which would restrict the file types accepted as one of the popular image types (jpeg, png, bmp, gif, or svg). Since we want to allow the user to upload any type of file, we’ll just stick to the file check limit.

That’s all there is to the request object. Its main purpose is simply to maintain an acceptable set of criteria (constraints) that the form body parameters must satisfy in order to delve deeper into the application. It should also be noted that these two fields (userFile and filename) must also be specified inside the HTML as input fields (with the field name matching the name inside the request object).

You might ask: of course, this defines the characteristics of what a form request should contain, but where is the actual validation of these constraints done? We will deal with this further.

Generating a request

Before we go ahead and make a few changes to the generated UploadController stub, I think it makes more sense to create a request class first. This is because the controller method that handles the request must enter a hint for the request object in its signature, which will allow it to automatically validate the form input (as specified in the rules () method. More on that later …) let’s use the command again artisan to stub our request:

php artisan make: request UploadFileRequest

This command will create a file named UploadFileRequest inside the app / Http / Requests / UploadFileRequest. Open the stub and take a look … you will find it very simple, containing only two methods, authorize () and rules.

What is a controller in laravel?

The controller is the “C” in the “MVC” (Model-View-Controller) architecture that Laravel is based on. A controller can be reduced to this simple definition: it receives a request from the client and returns a response to the client. This is a simple definition as well as the minimum requirements for any given controller. What it does in between the two is usually seen as an “action” by the controller (or “route implementation”). It acts as a second entry point into the application (the first is the request) for the client, which sends the request payload (which we will receive next) to the application, waiting for some type of response (in the form of a success page, redirect, error page, or any other kind of HTTP response) ).

The controller does (basically) the same as defining a route, with an anonymous function set to “action” when that route is reached. The difference is that the controller does a good job of separating concerns, while the route is determined according to the actual URL definition, which basically means we associate the assigned route URI with the route implementation, or the code that gets executed when that route is hit …

For example, the following two code snippets will achieve the same:

Example # 1: defining and implementing a route within one method call (in the web.php routes file)

// inside routes / web.php
<? php
Route :: get ('/ hello-world', function (Request $ request) {
   $ name = $ request-> name;
   return response () -> make ("<h1> Hello World! This is". $ name, 200);
});

Example # 2. The route definition is in route / web.php, but its implementation is in the class / app / Http / Controllers / HelloWorldController

// inside routes / web.php
<? php

Route :: get ('/ hello-world', 'HelloWorldController @ index') -> name ('hello-world');

-------------------------------------------------- ----------------------------------
// inside app / Http / Controllers / HelloWorldController.php
<? php
namespace App \ Http \ Controllers;
use Illuminate \ Http \ Request;

class HelloWorldController extends Controller
{
   public function index (Request $ request)
   {
       $ name = $ request-> name;
       return response () -> make ("<h1> Hello World! This is". $ name, 200);
   }
}

While example # 2 looks much more time consuming (and it isn’t – just a little more code – that’s it), let’s take a look at the benefits we get by instead placing our action logic for a given “hello-world” route inside a controller from the route definition into as a callback function:

  1. Our logic is clearly divided into a class of its own (separation of interests)
  2. Our controller will be configured to extend later if we need to add more functionality to it … Let’s say maybe we want to add a goodbye function … In this case, we’ll rename the controller to the more generic “HelloController” then define two separate methods, hello () and goodbye () . We would also need to define two separate routes that map the / hello and / goodbye URIs to their respective methods on the controller. This is desirable compared to populating a routes file with the implementation of each route defined as a callback function.
  3. Laravel has a built-in ability to cache all route definitions in an application to speed up the time it takes to find a given route (increases application performance); however, you will only be able to take advantage of this if all the routes you define within the application are configured using controller-specific bindings (see example # 2 above)

Let’s run this command, which will generate a new controller for us.

// ... inside the project's root directory:
php artisan make: controller UploadController   

Basically, this command generates a stub for a controller named “UploadController” inside the main controller directory at /app/Http/Controllers/UploadController.php. Feel free to open this file and see. This is very easy because it is only a mocked version of the controller with the correct namespace path and the required classes from which it extends.

What is a route in laravel?

A route in Laravel is essentially an endpoint specified by a URI that acts as a “pointer” to some piece of functionality offered by the application. More often than not, the route simply points to a method on the controller and also indicates which HTTP methods can use that URI. Route also does not always mean a controller method; it can simply pass the execution of the application to a specific Closure function or anonymous function.

Why use a route?

Routes are stored in files in the / routes folder inside the project root. By default, there are several different files corresponding to different “sides” of the application (“sides” come from the hexagonal architecture methodology). These include:

  • web.php Routes based on public browsers. These are the most common and this is what gets into the web browser. They go through the web middleware group and also contain csrf protections (which help defend against malicious attacks and form-based hacks) and usually contain some degree of “state” (by which I mean they use sessions)
  • api.php Routes that match the API group and therefore have API middleware enabled by default. These routes are stateless and have no session or cross-request memory (one request does not share data or memory with any other request – each is itself encapsulated).
  • console.php These routes correspond to the custom artisan commands you have created for your application.
  • channel.php Registers routes for broadcasting events

The key file to consider at this point is the browser, web.php. By default, one route is already defined, which you take when navigating to the web root of your application (the web directory is in the public directory). For the download application to work, we need three different routes:

  • / upload This will be the URI of the main page that displays our web form for uploading files.
  • / process This will be the place where the form, located in the / upload URI, submits its data submitted by the form (the “action” of the form)
  • / list This will list all files uploaded to the site

Note . The / list endpoint may not be needed if we want to put all the logic for displaying the upload form and the file list on the same page, but I’m keeping them separate for now to add some more stuff to the topic in hand.

// inside routes / web.php
Route :: get ('/ upload', 'UploadController @ upload') -> name ('upload');
Route :: get ('/ download,' UploadController @ download) -> name ('download');
Route :: post ('/ process', 'UploadController @ process') -> name ('process');
Route :: get ('/ list', 'UploadController @ list') -> name ('list');

For each desired route, we explicitly list it in the web.php routes file using one of the available HTTP specific request methods (get (), post (), put (), delete (), patch (), or options ()). For a breakdown of each one, check this . These methods determine which HTTP verbs are allowed to access a given route. If you need a route to be able to accept more than one HTTP verb (which might be the case if you are using a single page to display both the original data and post the submitted form data), you can use the Route :: any () method …

The second argument to Route :: get () and Route :: post () (and any other method associated with the HTTP verb on the Route facade) is the name of the specific controller and the method that is placed inside it. a controller that starts when the route endpoint is reached by an allowed HTTP request (GET, POST, PATCH, etc.). We use UploadController for all three routes and set them like this:

The last method that we call on each route is its name () function, which takes one string as an argument and is used to more or less “mark” a specific route by simply remembering the name (in our cases, load, process and list) … I realize it’s not such a good feature to give each route its own name when the url is exactly the same, but it’s really handy when you have a specific route like / users / profile / dashboard / config, which was would be easier to remember as profile-admin or user-config.

Note on the facades:

  • Facades provide a “static” interface for the classes that are available in the application’s service container. “
  • They provide a concise, memorable syntax that allows you to use Laravel functions without having to remember long class names that need to be entered or configured manually.

In the above route definitions, we are using the Route facade instead of manually instantiating a new Illuminate / Routing / Router object and calling the appropriate methods on that object. It’s just a shortcut that saves typing. Facades are heavily used throughout the Laravel framework – you can and should become more familiar with them. 

Application design: quickly reducing our requirements

In this online tutorial, we will create a very simple application that will only do two things:

  1. handle file uploads from web form
  2. display of previously uploaded files on another page.

For this project, our application will be write-only. This means that the user can only write files and view the list of downloaded files. This application is very simple, but it should serve as a good practice to start developing your Laravel skills and knowledge. Note that I’ve ruled out any modeling, migration, and database authentication for brevity, but in a real application these are additional things you’ll want to consider.

Here is a list of the components we need to get the app to work properly:

    • A route that will allow the outside world (Internet) to use the application, and also specify an endpoint that will indicate where the logic for saving the downloaded file is located
  • The controller that handles the response stream request
  • The template that will be used to display a list of previously uploaded files and the actual upload form itself
  • Request for what the controller will use to validate the data submitted from the web form

 

How to download and install Laravel using Composer

 It assumes that a copy of PHP is already installed on your local system. If not, you can read how to install it here

Composer is a package and dependency manager. To install it, open a terminal and change to a new directory. Run this command:

curl -Ss getcomposer.org/installer | php

The results of this command will look like this:

Note. For more detailed instructions on setting up Laravel, see the Laravel documentation here .

You will see how it downloads and compiles the composer.phar script that we use to install Laravel. While there are many ways to set up a new Laravel application, we will do it using the Laravel Composer script. To install this script, run:

composer global require laravel / installer

Which will look something like this:

This will download and install all the framework files, as well as any dependencies it requires. The packages will be saved in the vendor directory. Once it’s downloaded and installed, you can run the following command:

laravel new uploadApp

You will see something like the following output:

Composer installs all the packages that Laravel needs to run. This may take a few minutes, so please be patient. When finished, run ls -al to see what has been installed.

Here’s a quick breakdown of directories in a typical Laravel application:

  • app /: This is the source folder where our application code lives. All controllers, policies and models are in this folder
  • bootstrap /: contains the application startup script and some classmap files
  • config /: Contains application configuration files. They are usually not directly modified, but instead rely on the values ​​set in the .env (environment) file at the root of the application.
  • database /: contains database files including migrations, seeds and test factories
  • public /: a public folder containing the compiled resources and of course the index.php file
  • resources /: contains external resources such as JavaScript files, language files, CSS / SASS files and all templates used in the application (so called blade templates)
  • routes /: all routes in the app are inside here. There are several different “scopes” of routes, but we will focus on the web.php file.
  • storage /: All temporary cache files used by the application, session files, compiled view scripts, and log files.
  • tests /: Contains test files for the application, such as unit tests and functional tests.
  • vendor /: all dependency packages installed with composer

Now, let’s create the rest of the application and run it with a dedicated artisan command (to save ourselves the hassle of installing and configuring a web server like Apache or nginx). The .env file contains all the configuration values ​​that the files in the / config directory use to configure the application. Internally, you will notice that the configuration value for various parameters is used internally by the application.

Why Laravel?

Around 2000, most PHP code was procedural and could be found in the form of “scripts” that would be a mess of spaghetti code. Even on the simplest pages, there was no separation of concerns , and thus it was fairly easy for the application to quickly turn into a maintenance nightmare. The World Needs Something Better … Enter PHP version 5 and a host of PHP frameworks trying to bring much needed resolution and better solutions to various web application needs.

Since then, we’ve seen many frameworks released that would pave the way for the popular frameworks that exist and are in use today. Today, the top three (in our opinion) will include Zend Framework, Symfony and, of course, Laravel. While each of these platforms was based on similar principles and focused on solving (mostly) the same common problems, their key differences lie in their implementation. They each have their own quirks about how to solve problems. When you look at the code generated by each of them, you will see that there is a rather solid line separating them from each other. In our humble opinion, Laravel is the best.