Laravel -> Table of Contents: A) Composer 1) Do I Even Have Composer? 2) Composer Installation B) Laravel Installation 1) Laravel Version 2) Installation C) PHP Storm / Laravel Plugins A) Set Up Directory / Install Laravel Infrastructure B) Virtual Host 1) Apache 2) host file A) app 1) Console 2) http a) Controllers b) Middleware B) bootstrap C) config D) database E) public F) resources 1) assets 2) lang 3) views G) routes H) storage I) tests J) vendor K) env A) web.php (route to view) B) PublicController (route to controller) C) Groups D) Passing Parameters in Your Routes 1) Single Parameter Using a Route 2) Single Parameter Using Controller 3) Multiple Parameters Using Controller E) Namespaces (organizing your Controllers) 1) Create Controller 2) Create Route 3) Document Namespace and "use" F) Route Names 1) Passing a Variable 2) Passing Multiple Variables i) Route G) Redirect A) Create Middleware (Terminal Command) B) Register Middleware C) Use Your Middleware 1) Kernel.php 2) web.php (routes) D) Assign Middleware A) Connection B) Migration C) Migration Functions 1) Change Column Name 2) Change Default Value C) Models D) Seeders, Factories and Faker 1) Factories 2) Seeder A) Master View B) Passing Variables C) Passing Variables Globally 1) AppServiceProvider.php 2) .env file D) Loops E) Includes F) Helpers / Active Links A) contact.blade.php B) Route C) Controller 1) Single Field D) File Upload 1) File Upload Field 2) Controller E) How to Handle an Array of Inputs A) Basic Setup B) Import Front End Assets C) Create Other Pages 1) Create Route 2) Create PublicController 3) Create View 4) Create master.blade.php D) Asset Helper E) Authentication 1) mailtrap.io F) Navigation 1) navigation.blade.php G) Admin Panel Assets 1) AdminController 2) Admin Route 3) Dashboard Blade H) Protecting Admin Panel 1) Create and Register CheckRole Middleware 2) Edit CheckRole Middleware a) Create CheckRole b) Register CheckRole c) Run CheckRole I) Login Page 1) auth.blade.php 2) login.blade.php J) Dashboard K) Dashboard Navigation 1) Login Name 2) Sidebar Links 3) Logout L) Creating, Migrating, Seeding...Posts and Comments Table M) Displaying Posts & Comments 1) Dynamic Title 2) welcome.blade.php / Post.php Model / Public Controller N) Displaying Single Post & Comments A) Setting Up the Database and Database Content 1) composer create-project (import from Github) 2) database / .env file 3) migration i) create migration file / adust AppServiceProvider.php ii) create seeder file iii) Adjust DatabaseSeeder.php File iv) Make Article Factory v) Make Model vi) Create Tables vii) Add Dummy Data B) Setting Up Controllers, Routes & Resources 1) List Articles i) ArticlesController.php ii) api.php (routes) iii) Resource iv) Postman / API in Action 2) Display Article 3) Add / Edit Article 4) Delete Article C) Vue.js 1) Install Dependencies and "npm run watch" i) Bringing Files from One Project to Another ii) package.json iii) composer.json 2) Basic Files i) npm run watch ii) welcome.blade.php iii) app.js iv) Articles.vue 3) Cadillac Display 4) Delete Article 5) Add Article Form A) Incorporating Security 1) Setup Database B) Positioning of Login | Register Links 1) welcome.blade.php 2) style.css C) User Login and Administrator D) Pages 1) Pages Table 2) Insert Pages
A) Composer (back to top...) 1) Do I Even Have Composer? (back to top...) To find out if you even have Composer installed, just run the following: composer Pretty simple. If you've got it installed, you'll get this:
$ composer ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 1.8.4 2019-02-11 10:52:10 Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question --profile Display timing and memory usage information --no-plugins Whether to disable plugins. -d, --working-dir=WORKING-DIR If specified, use the given directory as working directory. -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: about Shows the short information about Composer. archive Creates an archive of this composer package. browse Opens the package's repository URL or homepage in your browser. check-platform-reqs Check that platform requirements are satisfied. clear-cache Clears composer's internal package cache. clearcache Clears composer's internal package cache. config Sets config options. create-project Creates new project from a package into given directory. depends Shows which packages cause the given package to be installed. diagnose Diagnoses the system to identify common errors. dump-autoload Dumps the autoloader. dumpautoload Dumps the autoloader. exec Executes a vendored binary/script. global Allows running commands in the global composer dir ($COMPOSER_HOME). help Displays help for a command home Opens the package's repository URL or homepage in your browser. i Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. info Shows information about packages. init Creates a basic composer.json file in current directory. install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. licenses Shows information about licenses of dependencies. list Lists commands outdated Shows a list of installed packages that have updates available, including their latest version. prohibits Shows which packages prevent the given package from being installed. remove Removes a package from the require or require-dev. require Adds required packages to your composer.json and installs them. run-script Runs the scripts defined in composer.json. search Searches for packages. self-update Updates composer.phar to the latest version. selfupdate Updates composer.phar to the latest version. show Shows information about packages. status Shows a list of locally modified packages, for packages installed from source. suggests Shows package suggestions. u Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. update Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. upgrade Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file. validate Validates a composer.json and composer.lock. why Shows which packages cause the given package to be installed. why-not Shows which packages prevent the given package from being installed.
Otherwise, you'll just get $composer bash: composer: command not found 2) Composer Installation (back to top...) You'll use the Composer installer which will pretty much take care of everything. Don't worry about the "developer" option. Just install it and you'll be gold. That will create all the files and dependencies you need to start you new Laravel project. The only thing that you need to ensure is that when the prompt comes up that declares the PATH to your php.exe file - that path needs to be accurate. Provided that's in place, you'll be able to successfully call Composer from anywhere. And don't worry about a "proxy server." Just blow past that for now. Ran into a little problem with Composer when I first set it up. Wound up deleting it, reinstalling it and making a point of closing down the Command Prompt before attempting to set up a Laravel project. After that everything worked great. B) Laravel Installation (back to top...) 1) Laravel Version (back to top...) To determine what version of Laravel you're running, navigate to the directory where you've got your Laravel app and type this into your Command Line: php artisan --version Make sure you're in the directory where your Laravel application is located. Otherwise, you'll get a response that suggests you don't even have Laravel installed. 2) Installation (back to top...) To install Laravel, type this in your Command Line (again, shut down your Command Line app and open it up again to ensure that the Composer dynamic you just installed is recognized by Command Line as being present).
composer global require laravel/installer
That will install Laravel. Quit your Command Line interface and restart it. Type in laravel -h to see if you're running Laravel and if you get a screen that lists a number of Laravel commands, you're all set! Now you're ready to begin your project! You can also go out to https://laravel.com/docs/5.8 and copy and paste this line of code that you can retrieve from that site C) PHP Storm / Laravel Plugins (back to top...) You can install some "Laravel Helpers" that allow PHP Storm to fill in some gaps and make your coding a little easier. Do this by going to "File -> Settings" and navigate to "Plugins." Do a search for "Larvel" and install it. After that, you'll want to navigate to "File -> Settings -> Languages & Frameworks -> Laravel" and activate that plugin for your project (see image to the right). A) Set Up Directory / Install Laravel Infrastructure (back to top...) Use this command to set up your directory and the necessary files to kick off your project.
C:\wamp64\www>composer create-project laravel/laravel new blog
B) Virtual Host (back to top...) What you're doing here is setting up your "virtual host" (localhost) in a way that resembles the manner in which your actual user is going to access your site. You've got to adjust two files to make that happen. The goal is to bring up the "blog" site in your "localhost" without having to type http://localhost/blog/public. We're going to change that so all we need to do is type blog.test. 1) Apache (back to top...) Right now, provided your WAMP was running, you could go to http://localhost/blog/public/ and everything would be running just fine. For the sake of this tutorial, however, we're going to adjust our Apache httpd-vhost.conf file to accommodate a more streamlined URL. Go to C:\wamp64\bin\apache\apache2.4.23\conf\extra and navigate to your "httpd-vhost.conf" file and add this line:
<VirtualHost *:80> ServerName blog.test DocumentRoot c:/wamp/www/blog/public </VirtualHost>
2) host file (back to top...) Now, open up a Notepad application and choose "Run as Administrator." Navigate to your C:\Windows\System32\drivers\etc and open up your host file. You'll most likely have to choose the "Show All Files" option in order to see it. Add this line: 127.0.0.1 blog.test. Restart Apache and you should be gold! All you need to do is head out to "blog.test." The first page you're looking at is based off of the routes/web.php file which has this: Route::get('/', function () { return view('welcome'); }); That's opening up the welcome.blade.php page that sitting in resources/views. "blade," by the way, is the template engine that Larvavel uses. A) app (back to top...) 1) Console (back to top...) This is where you'll store your Console commands will live. You won't be using this directory that often. 2) http (back to top...) a) Controllers (back to top...) This is where your logic lives as per your typical MVC architecture. b) Middleware (back to top...) Middleware is the home for that code you may want to execute before the looks in the Controller directory. A good example would be any kind of authentication you want in place. B) bootstrap (back to top...) This is where you're going to keep your "cache" file. C) config (back to top...) This is what you would expect, as far as you "config" settings. An example would be your "database.php" file which has all the settings you would want access to in order to connect with any one of a number of database types. D) database (back to top...) "migrations" let you manage your database structures. "factories" and "seeds" allow you to do "en masse" operations. We'll get into that more later. E) public (back to top...) This is your root directory and the only place that's accessible to the client browser. F) resources (back to top...) 1) assets (back to top...) The "assets" directory is where you'll keep all of your JavaScript code that needs to be compiled. 2) lang (back to top...) "lang" is where you'll put your translated words if you're building a multi-language site. 3) views (back to top...) Here's your "home" for all of your "views" - what your user is going to "see." G) routes (back to top...) The only file you need to worry about right now is "web.php." This is where you're going to put your main "routing" for your site. H) storage (back to top...) The only directory you need to concern yourself with for right now is the "logs" directory. That's where you're storing your system error logs. I) tests (back to top...) This is where you'll keep your unit and functional tests. We'll get into that more later... J) vendor (back to top...) Here's where you're keeping all of your supplementary tech. You'll never edit anything in here. K) env (back to top...) "env" is a file you want to remember in that it's here where you define all of your database constants. "database.php" is the code, but "env" is where you store your values.
APP_NAME=Laravel APP_ENV=local APP_KEY=base64:UWnrNlE2Ut5/SevmLpcwp4xx1H9fLt5Hx5DmFLhJBXM= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=homestead DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
A) web.php (back to top...) This is your primary / go to route. Let's pop the hood on it and see where it leads: Route::get('/', function () { return view('welcome'); }); This is going to be your "index" page. You'll see that it shoots you to the "welcome" view. This is the "welcome.blade.php:"
<!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/css?family=Nunito:200,600" rel="stylesheet"> <!-- Styles --> <style> html, body { background-color: #fff; color: #636b6f; font-family: 'Nunito', sans-serif; font-weight: 200; height: 100vh; margin: 0; } .full-height { height: 100vh; } .flex-center { align-items: center; display: flex; justify-content: center; } .position-ref { position: relative; } .top-right { position: absolute; right: 10px; top: 18px; } .content { text-align: center; } .title { font-size: 84px; } .links > a { color: #636b6f; padding: 0 25px; font-size: 13px; font-weight: 600; letter-spacing: .1rem; text-decoration: none; text-transform: uppercase; } .m-b-md { margin-bottom: 30px; } </style> </head> <body> <div class="flex-center position-ref full-height"> @if (Route::has('login')) <div class="top-right links"> @auth <a href="{{ url('/home') }}">Home</a> @else <a href="{{ route('login') }}">Login</a> @if (Route::has('register')) <a href="{{ route('register') }}">Register</a> @endif @endauth </div> @endif <div class="content"> <div class="title m-b-md"> Laravel </div> <div class="links"> <a href="https://laravel.com/docs">Docs</a> <a href="https://laracasts.com">Laracasts</a> <a href="https://laravel-news.com">News</a> <a href="https://blog.laravel.com">Blog</a> <a href="https://nova.laravel.com">Nova</a> <a href="https://forge.laravel.com">Forge</a> <a href="https://github.com/laravel/laravel">GitHub</a> </div> </div> </div> </body> </html>
Get it? Now, as a rule, you don't want to use "logic" code to return a basic view. You're better off writing this in your "web.php" page: Route::view('/', 'welcome'); B) PublicController.php (back to top...) Instead of using the "view" approach right out to the box, let's route things to a Controller and then, from there, direct it to the View. First, create a Controller using your Terminal:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:controller PublicController
Here's your route that references the Controller: Route::get('/', "PublicController@index"); ...and now, here's our "PublicController" with the "index" function that returns the "welcome" view: <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class PublicController extends Controller { public function index() { return view('welcome'); } } C) Groups (back to top...) This is a great way to organize your routes according a particular URL. For example, all of your "blog/admin" routes could be grouped like this:
Route::get('/', "PublicController@index"); Route::prefix('admin')->group(function(){ Route::get('users', function(){ return "admin users"; }); Route::get('posts', function(){ return "admin posts"; }); });
D) Passing Parameters in Your Routes (back to top...) 1) Single Parameter Using a Route (back to top...) To pass a parameter to your page using your Routes, you would do this: Route::get('user/{userId}', function($userId){ return 'user id: ' . $userId; }); So, if your URL is "http://blog.test/admin/user/1," that will give you this:
user id: 1
2) Single Parameter Using Controller (back to top...) You can also do the same thing using your Controller. Here's your Route: Route::get('user/{userId}', 'PublicController@userInfo'); ...and here's your Controller: public function userInfo($userId) { return "User ID: ".$userId; } Same result as before. Finally, if you wanted to pass multiple variables in through your URL, you would do this: 3) Multiple Parameters Using Controller (back to top...) Here's your Controller: public function userInfo($userId, $name) { return "User ID: ".$userId. '-' . $name; } ...and here's your Router: Route::get('user/{userId}/{name}', 'PublicController@userInfo'); E) Namespaces (organizing your Controllers) (back to top...) Right now you only have one Controller. That can get pretty cumbersome pretty quick. To break things up a bit so you can keep your code from getting out of control, you use "namespaces." 1) Create Controller (back to top...) Start by creating another Controller. Call it "UsersController.php."
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:controller UsersController
Now, create another directory in your "Controllers" folder and name it "Admin" and move your new Controller to that directory (see image to the right). 2) Create Route (back to top...) Here's your Route: Route::prefix('admin')->group(function(){ Route::get('users', 'Admin\UsersController@listUsers'); Route::get('posts', function(){ return "admin posts"; }); Notice the fact that it has an "address" and the fact that the "\" is a backward slash. You can't use a forward slash. a) Namespace Route Shortcut (back to top...) You can also do this: Route::namespace('Admin')->prefix('admin')->group(function(){ Route::get('users', 'UsersController@listUsers'); Route::get('posts', function(){ return "admin posts"; }); Notice the addition of the namespace dynamic. Plus, you don't have the "Admin" entity like you did before... Here's what it was: Route::get('users', 'Admin\UsersController@listUsers'); Here's what you've got now: Route::get('users', 'UsersController@listUsers'); A neat little way to keep things organized without having to document the namespace over and over again... 3) Document Namespace and "use" (back to top...) ...and here's your Controller: <?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UsersController extends Controller { public function listUsers() { return "list users = admin/users controller"; } } Notice the way the "namespace" is documented and the way "use" is referenced as well. And that will do it! F) Route Names (back to top...) Hardcoding a specific URL isn't necessarily a good idea, especially if you wind up changing your domain. You can "name" your routes so you don't have to worry about specifying a particular domain. Here's how you do it: Route::get('/', "PublicController@index")->name('welcome'); //here I'm naming my route Route::get('posts', function(){ return route('welcome'); }); Now, when I go out to "http://blog.test/admin/posts," I get this:
http://blog.test
...and that makes sense because the whole route is based on "/"... 1) Passing a Variable (back to top...) If you had to pass a variable through your route name, you would do it this way: Here's your "web.php" page:
<?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/{name}', "PublicController@index")->name('welcome'); Route::namespace('Admin')->prefix('admin')->group(function(){ Route::get('users', 'UsersController@listUsers'); Route::get('posts', function(){ return route(name: 'welcome', parameters: "Bruce"); }); });
Your Controller looks like this: public function index($name) { return view('welcome'); } So, now when you go out to "http://blog.test/admin/posts," you get this:
http://blog.test/Bruce
2) Passing Multiple Variables (back to top...) i) Route (back to top...) To pass mulitple variables, you use an array configuration. Here's your Route:
<?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/{name}/{$age}',"PublicController@index")->name('welcome'); Route::namespace('Admin')->prefix('admin')->group(function(){ Route::get('users', 'UsersController@listUsers'); Route::get('posts', function(){ return route('welcome', ['name'=>'Bruce', 'age' => '56']); }); });
...and then your page looks like this:
http://blog.test/Bruce/56
Notice that you don't have to worry about your Controller, as far as populating the Route with content. G) Redirect (back to top...) Easy - peasy... Route::redirect('/old', '/new', 301); A) Create Middleware (Terminal Command) (back to top...) To create your Middleware functionality, type this into your Terminal:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:middleware CheckAge Middleware created successfully.
B) Register Middleware (back to top...) 1) Kernel.php (back to top...) In your "Http" directory, you've got a file called, "Kernel.php." Here's where you're going to register your Middleware. You've got more than one option. The one we're going with in this case is the one that allows you to assign it to a group or an individual route:
/** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array */ protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'checkAge' => \App\Http\Middleware\CheckAge::class ];
2) web.php (routes) (back to top...) Now, assign it to your route... Route::get('users', 'UsersController@listUsers')->middleware('checkAge'); Now, when you put this in your URL: http://blog.test/admin/users?age=25 ...you get this page:
list users = admin/users controller
D) Assign Middleware (back to top...) Previously, you assigned some Middleware to a specific route. You can also assign it to a Controller which will affect multiple routes. You just put it in the corresponding Controller. In this case, we're altering the "UsersController.php."
<?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class UsersController extends Controller { public function __construct() { $this->middleware('checkAge'); } public function listUsers() { return "list users = admin/users controller"; } }
A) Set up Your Database Connection (back to top...) To set up your database connection, you're going to go to your "env" file and do this: DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=blog DB_USERNAME=root DB_PASSWORD= That's your "blog" database with the necessary credentials. B) Migration (back to top...) Alright...what's a "migration?" Here's the definition from the Laravel docs...
Migrations are like version control for your database, allowing your team to easily modify and share the application's database schema. Migrations are typically paired with Laravel's schema builder to easily build your application's database schema. If you have ever had to tell a teammate to manually add a column to their local database schema, you've faced the problem that database migrations solve.
Here's the way it looks in real life. The command is create_posts_table "posts" is the name of your table. That's the first part of the command. The second part is what you see, as far as the --create posts. With this command, you're not just creating a table, you're doing it in a way that's documented so other people working on the same app are alterted to what you've done.
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:migration create_posts_table --create=posts
After you do that, you'll see a record of that in your database->migrations file. It looks like this:
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->bigIncrements('id'); $table->string( 'title')->default('my title'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('posts'); } }
"up" is what's triggered when you execute the migration, "down" is when you want to roll things back. this is the syntax you use to create a default value Now, having created your "migration" dynamic, all you need to do now is run:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan migrate
...and just like that, you're table is created along with another table that documents your migrations. Pretty cool! And, if you want to roll all that back, all you need to do is:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan migrate:rollback
...and that will kill your "posts" table. C) Migration Functions (back to top...) 1) Change Column Name (back to top...) You can use your terminal to rename columns in your database, although you need a new package to do that. Using "composer," you'll type this into your terminal:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ composer require doctrine/dbal
\Once that's installed, you'll do this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:migration rename_content_to_body --table=posts
Understand, this is just manufacturing a "migration" doc. This doesn't actually change the name of the column. To do that, you've got to go to the newly formed migration file named, "rename_content_to_body." Once you're there, you'll add this syntax:
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class RenameContentToBody extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->renameColumn('content', 'body'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { $table->renameColumn('body', 'content'); }); } }
Now when you do this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan migrate
That will change the name of the column, "content" to "body." 2) Change Default Value (back to top...) You can change the default value of your column by first creating your migration file...
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:migration change_default_value_of_title --table=posts
...and then entering the highlighted syntax to that new migration file:
<p?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class ChangeDefaultValueOfTitle extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->string( 'title')->default('Nice title')->change(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { // }); } }
C) Models (back to top...) With Laravel you can create your Model right out of the chute that will interact with the table that you name it after. Like this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:model Post
The name "Post," will automatically assume you're going to be interacting with the "post" table. D) Seeders, Factories and Faker (back to top...) 1) Factories (back to top...) Factories are a feature that Laravel provides to help you test your data. You start by using your Terminal to create a "factory" file:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:factory PostFactory
Once it's made, you'll document the code like this in your new file:
<?php /* @var $factory \Illuminate\Database\Eloquent\Factory */ use App\Model; use Faker\Generator as Faker; /* @var $factory \Illuminate\Database\Eloquent\Factory */ $factory->define(App\Post::class, function(Faker $faker) { return [ 'title' => \Faker\Provider\Lorem::sentence($nbWords = 6, $variableNbWords = true), 'body' => \Faker\Provider\Lorem::paragraph($nbSentences = 3, $variableNbSentences = true) ]; });
2) Seeder (back to top...) Now, create your "seeder" file:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan make:seeder PostSeeder
Now, once that file has been created, here's the code:
<?php use Illuminate\Database\Seeder; class PostSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { factory(App\Post::class, 10)->create()->each(function($u) { $u->issues()->save(factory(App\Post)::class)-> make()); }); }
this first line is saying, "Create a 'post' function that will insert 10 rows. Each of those rows will be... a saved "issue" that's associated with each of the created posts. In order to get our "seed" class recognized, we'll need,"dump autoload" which you'll install via Composer:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ composer dump-autoload
At this point, you're going to use the "DatabaseSeeder.php" page to "seed" your database. You can document things on the actual php page, or you can do it via the Terminal like this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/blog $ php artisan db:seed --class=PostSeeder
And that will populate your database with 10 rows of dummy data! A) Master View (back to top...) First, you've got your "web.php" which is your "route" configuration:
<?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('posts', "PublicController@displayPosts")->name('displayPosts'); Route::namespace('Admin')->prefix('admin')->group(function(){ Route::get('users', 'UsersController@listUsers'); Route::get('posts', function(){ return route('welcome', ['name' => 'Bruce', 'age' => '56']); }); }); Route::view('/', 'welcome'); Route::view('/about', 'about');
The highlighted "welcome" route is what's triggered when your user hits the index page ('/'). When you go to the "welcome.blade.php" page, you see this:
@extends('Layouts.master') @section('content') <div class="title m-b-md"> Laravel </div> <div class="links"> <a href="https://laravel.com/docs">Docs</a> <a href="https://laracasts.com">Laracasts</a> <a href="https://laravel-news.com">News</a> <a href="https://blog.laravel.com">Blog</a> <a href="https://nova.laravel.com">Nova</a> <a href="https://forge.laravel.com">Forge</a> <a href="https://github.com/laravel/laravel">GitHub</a> </div> @endsection
@extends('Layouts.master') is the code you're using to tell the system to "include" the code that's coming from your "views/layouts/master.blade.php" file. Notice the convention as far as what's capitalized and what is written in lowercase etc. @section('content') is the content that's specific to this particular page. B) Passing Variables (back to top...) This is pretty cool. To pass a variable into your view, you're going to start by adjusting your "web.php" page and configure your route to your index page like this: //Route::view('/', 'welcome'); Route::get('/', 'PublicController@index'); Route::view('/about', 'about'); Instead of calling a "view" right out of the chute, we're going to call the PublicController and we're going to be looking for the "index" method. That looks like this:
class PublicController extends Controller { public function index() { $title = "Muscular Christianity"; return view('welcome', compact( 'title')); } }
we start by declaring a variable which, in this case, is "title." Then we give it a value. we use the "compact" function to grab and send the variable to the "welcome" view. Finally, on the "welcome.blade.php" page, we've got this:
@extends('Layouts.master') @section('content') <div class="title m-b-md"> {{ $title }} </div> <div class="links"> <a href="https://laravel.com/docs">Docs</a> <a href="https://laracasts.com">Laracasts</a> <a href="https://laravel-news.com">News</a> <a href="https://blog.laravel.com">Blog</a> <a href="https://nova.laravel.com">Nova</a> <a href="https://forge.laravel.com">Forge</a> <a href="https://github.com/laravel/laravel">GitHub</a> </div> @endsection
You can also pass HTML code into your $title element by doing this: Here's your Controller: <h1>Muscular Christianity</> ...and then on your "welcome.blade.php" page, you'll have this: {!! $title !!}. Strange, but it works! Here's the thing: While you can pass HTML into your views, a more secure way of doing it would be like this: Here's you Controller:
class PublicController extends Controller { public function index() { $title = "Muscular Christianity"; $title]); } }
...and here's your "welcome.blade.php..." <div class="title m-b-md"> {{$mainTitle}} </div> C) Passing Variables Globally (back to top...) You can pass variables globally as well. You start with your AppServiceProvider.php page in your app/providers directory 1) AppServiceProvider.php
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\View; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { View::share('title', 'Muscular Christianity'); } }
make sure you add this package at the top this is the code that passes your variable Now, when you go into any of your "blades," you can do this: <div class="title m-b-md"> {{ $title }} </div> 2) .env file (back to top...) Another way to pass global variables, like the title, is to use your .env file which is located in your home directory. APP_NAME=Laravel APP_ENV=local APP_KEY=base64:oe3Ry9cOza5diYON16y9DlUbVDQu8/uIMzU7rODrrNE= APP_DEBUG=true APP_URL=http://localhost And then you reference that in your "AppServiceProvider.php" page like this: public function boot() { View::share('title', env('APP_NAME')); } D) Loops (back to top...) This is your PublicController.php:
class PublicController extends Controller { public function index() { $posts = Post::all(); return view('welcome', compact('posts')); } }
...and then we head out over to our "welcome.blade.php" page and we do this: <div class="content"> @foreach($posts as $post) <li>{{ $post }}</li> @endforeach </div> ...and that's that! E) Includes (back to top...) To demo includes we're going to create a "navigation.blade.php" page with some links: <div class="top-right links"> <a href="#">Welcome</a> <a href="#">About</a> </div> ...and then you reference that on your "master.blade.php" page like this: @include('includes/navigation'); F) Helpers / Active Links (back to top...) We're going to use a shorthand "IF" block to establish the style of the links based on the route name... <div class="top-right links"> <a {{ Request:: route()->getName() == 'welcome' ? 'style=color:red;' : ''}} href="{{ route('welcome') }}">Welcome</a> <a {{ Request:: route()->getName() == 'about' ? 'style=color:red;' : ''}} href="{{ route('about') }}">About</a> </div> A) contact.blade.php (back to top...) Here's our "contact.blade.php" page:
@extends('layouts.master') @section('content') <form method="Post" action="{{ route('contactPost') }}"> @csrf <div class="form-group"> <input class="form-control" type="text" placeholder="Name" name="name"> </div> <div class="form-group"> <input class="form-control" type="email" placeholder="Email" name="email"> </div> <div class="form-group"> <textarea class="form-control" id="" cols="30" rows="10" name="message" placeholder="Message..."></textarea> </div> <div class="form-group"> <button type="submit" class="form-control">Send message</button> </div> </form> @endsection
this is your route with this one little piece of code, you're adding a hidden field with a CSRF token B) Route (back to top...) Here's your route: Route::post('/contact', 'PublicController@contactPost')->name('contactPost'); The name of the route is "contactPost," the method it will be looking for on the PublicController is "contactPost." C) PublicController (back to top...) public function contactPost(Request $request) { var_dump($request->all()); } 1) Single Field (back to top...) To retrieve a single field, you would do this: public function contactPost(Request $request) { //var_dump($request->all()); //var_dump($request->input('name')); $name=$request['name']; var_dump($name); } D) File Upload (back to top...) 1) File Upload Field (back to top...) Once again, Laravel doesn't fail to impress with how much code you can write with just a little bit of syntax. <div class="form-group"> <input class="form-control" type="file" name="attachment"> </div> Of course, you don't want to forget to alter your form by including the "enctype..." <form method="Post" action="{{ route('contactPost') }}" enctype="multipart/form-data"> 2) Controller (back to top...) Here's the way your Controller will look like when you're simply returning the original name of the file: public function contactPost(Request $request) { if($request->hasFile('attachment')) { $file = $request->file('attachment'); echo "File Name " . $file->getClientOriginalName(); } else { return "no"; } $name=$request['name']; var_dump($name); } ...and here's how it's going to look when you actually move the file to a specific directory:
public function contactPost(Request $request) { if($request->hasFile('attachment')) { $file = $request->file('attachment'); $filename = $file->getClientOriginalName(); $file->move('images', $filename); } else { return "no"; }
see if you've got a file save the file itself in a variable called $file grab the file name using the funciont getClientOriginalName move the file to a directory called, "images" E) How to Handle an Array of Inputs (back to top...) Imagine you've got more than one email field. How do you handle that kind of scenario? For example, on your "contact" form you have both a personal email and a work email field... <div class="form-group"> <input class="form-control" type="email" placeholder="Personal Email" name="email[]"> </div> <div class="form-group"> <input class="form-control" type="email" placeholder="Work Email" name="email[]"> </div> Notice how you have the name of each field coded as "email[]." Now, when you retrieve those through you Controller, it will look like this:
public function contactPost(Request $request) { if($request->hasFile('attachment')) { $file = $request->file('attachment'); $filename = $file->getClientOriginalName(); $file->move('images', $filename); } $name=$request['email']; var_dump($name); }
Now, when you do a data dump based on the highlighted code, you get this: 0 => string 'bruce@brucegust.com' (length=19) 1 => string 'brucegust@gmail.com' (length=19) If you wanted to retrieve just the personal email, you would do this: $name=$request['email']; var_dump($name[0]); A) Basic Setup (back to top...) You're going to start by navigating to your home directory in your Terminal and typing in the "laravel new personal-blog." Notice this is different from the first project we set up in that we used Composer. But since Laravel is already installed, we can go this route.
C:\wamp64\www\ laravel new personal-blog
After that, you're going to want to configure your browser so it automatically goes to the correct home page. B) Import Front End Assets (back to top...) You can download an entire look and feel for free without having to start from scratch. BOOM! Head out to https://startbootstrap.com/themes/creative/ and download the zip file. Copy and paste all of the files into your "assets" directory. Then delete everything except the css, img, js and vendor folders. Grab the HTML code that is rendered when you go to "personal.blog/assest/index.html." Copy that into your "welcome.blade.php" page and then add "assets" to the appropriate URLs so your page renders correctly. Like this:
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom fonts for this template --> <link href="assets/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css"> <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link href="assets/css/clean-blog.min.css" rel="stylesheet">
You'll do that for all your CSS and JS directories. C) Create Other Pages (back to top...) 1) Create Route (back to top...) Start by creating your Route... Route::get('/', function () { return view('welcome'); }); ...becomes this: Route::get('/', 'PublicController@index'); 2) Create PublicController (back to top...) Since you don't have a PublicController file yet, you need to start with this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:controller PublicController
After you make that Controller, add this code to accommodate your page... class PublicController extends Controller { public function index() { return view('welcome'); } } 3) Create View (back to top...) You've already got the "welcome.blade.php" view page, so you're set. To add new pages, you'll repeat the above process sans the creation of the PublicController... You can get those pages by heading out to the following links: This is great, but if you were to look at the pages, you'll notice that the C SS is all squirreled up. To remedy that, we're going to create a "master.blade.php" page that will provide all of the CSS / layout dynamics that we need that we can then easily apply to all of the pages. 4) Create master.blade.php (back to top...) This is your "master.blade.php" page:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Clean Blog - Start Bootstrap Theme</title> <!-- Bootstrap core CSS --> <link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom fonts for this template --> <link href="assets/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css"> <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link href="assets/css/clean-blog.min.css" rel="stylesheet"> </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"> <div class="container"> <a class="navbar-brand" href="index.html">Start Bootstrap</a> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> Menu <i class="fas fa-bars"></i> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item"> <a class="nav-link" href="index.html">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="about.html">About</a> </li> <li class="nav-item"> <a class="nav-link" href="post.html">Sample Post</a> </li> <li class="nav-item"> <a class="nav-link" href="contact.html">Contact</a> </li> </ul> </div> </div> </nav> @yield('content'); <!-- Footer --> <footer> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <ul class="list-inline text-center"> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-twitter fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-github fa-stack-1x fa-inverse"></i> </span> </a> </li> </ul> <p class="copyright text-muted">Copyright © Your Website 2019</p> </div> </div> </div> </footer> <!-- Bootstrap core JavaScript --> <script src="assets/vendor/jquery/jquery.min.js"></script> <script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script> <!-- Custom scripts for this template --> <script src="assets/js/clean-blog.min.js"></script> </body> </html>
You created a new directory in your "views" folder and named it "layouts" and then put your "master.blade.php" page in that folder. Your "welcome.blade.php" page went from this:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Clean Blog - Start Bootstrap Theme</title> <!-- Bootstrap core CSS --> <link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom fonts for this template --> <link href="assets/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css"> <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link href="assets/css/clean-blog.min.css" rel="stylesheet"> </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"> <div class="container"> <a class="navbar-brand" href="index.html">Start Bootstrap</a> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> Menu <i class="fas fa-bars"></i> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item"> <a class="nav-link" href="index.html">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="about.html">About</a> </li> <li class="nav-item"> <a class="nav-link" href="post.html">Sample Post</a> </li> <li class="nav-item"> <a class="nav-link" href="contact.html">Contact</a> </li> </ul> </div> </div> </nav> <!-- Page Header --> <header class="masthead" style="background-image: url('img/home-bg.jpg')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Clean Blog</h1> <span class="subheading">A Blog Theme by Start Bootstrap</span> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Man must explore, and this is exploration at its greatest </h2> <h3 class="post-subtitle"> Problems look mighty small from 150 miles up </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on September 24, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> I believe every human has a finite number of heartbeats. I don't intend to waste any of mine. </h2> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on September 18, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Science has not yet mastered prophecy </h2> <h3 class="post-subtitle"> We predict too much for the next year and yet far too little for the next ten. </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on August 24, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Failure is not an option </h2> <h3 class="post-subtitle"> Many say exploration is part of our destiny, but it’s actually our duty to future generations. </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on July 8, 2019</p> </div> <hr> <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> <hr> <!-- Footer --> <footer> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <ul class="list-inline text-center"> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-twitter fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-github fa-stack-1x fa-inverse"></i> </span> </a> </li> </ul> <p class="copyright text-muted">Copyright © Your Website 2019</p> </div> </div> </div> </footer> <!-- Bootstrap core JavaScript --> <script src="assets/vendor/jquery/jquery.min.js"></script> <script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script> <!-- Custom scripts for this template --> <script src="assets/js/clean-blog.min.js"></script> </body> </html>
...to this:
@extends('layouts.master') @section('content') this is your dynamic content - the stuff that's unique to that particular page <!-- Page Header --> <header class="masthead" style="background-image: url('img/home-bg.jpg')"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="site-heading"> <h1>Clean Blog</h1> <span class="subheading">A Blog Theme by Start Bootstrap</span> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Man must explore, and this is exploration at its greatest </h2> <h3 class="post-subtitle"> Problems look mighty small from 150 miles up </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on September 24, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> I believe every human has a finite number of heartbeats. I don't intend to waste any of mine. </h2> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on September 18, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Science has not yet mastered prophecy </h2> <h3 class="post-subtitle"> We predict too much for the next year and yet far too little for the next ten. </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on August 24, 2019</p> </div> <hr> <div class="post-preview"> <a href="post.html"> <h2 class="post-title"> Failure is not an option </h2> <h3 class="post-subtitle"> Many say exploration is part of our destiny, but it’s actually our duty to future generations. </h3> </a> <p class="post-meta">Posted by <a href="#">Start Bootstrap</a> on July 8, 2019</p> </div> <hr> <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> @endsection
extend your master layout paradigm define your content (this is going to be referenced on your "master.blade.php" page which we'll see in a moment close your "content" section And this is your "master.blade.php" page:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>Clean Blog - Start Bootstrap Theme</title> <!-- Bootstrap core CSS --> <link href="/assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom fonts for this template --> <link href="/assets/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css"> <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'> <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'> <!-- Custom styles for this template --> <link href="/ssets/css/clean-blog.min.css" rel="stylesheet"> </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav"> <div class="container"> <a class="navbar-brand" href="index.html">Start Bootstrap</a> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> Menu <i class="fas fa-bars"></i> </button> <div class="collapse navbar-collapse" id="navbarResponsive"> <ul class="navbar-nav ml-auto"> <li class="nav-item"> <a class="nav-link" href="index.html">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="about.html">About</a> </li> <li class="nav-item"> <a class="nav-link" href="post.html">Sample Post</a> </li> <li class="nav-item"> <a class="nav-link" href="contact.html">Contact</a> </li> </ul> </div> </div> </nav> @yield('content'); <!-- Footer --> <footer> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <ul class="list-inline text-center"> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-twitter fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i> </span> </a> </li> <li class="list-inline-item"> <a href="#"> <span class="fa-stack fa-lg"> <i class="fas fa-circle fa-stack-2x"></i> <i class="fab fa-github fa-stack-1x fa-inverse"></i> </span> </a> </li> </ul> <p class="copyright text-muted">Copyright © Your Website 2019</p> </div> </div> </div> </footer> <!-- Bootstrap core JavaScript --> <script src="assets/vendor/jquery/jquery.min.js"></script> <script src="assets/vendor/bootstrap/js/bootstrap.bundle.min.js"></script> <!-- Custom scripts for this template --> <script src="assets/js/clean-blog.min.js"></script> </body> </html>
Notice that "assets" was altered by adding a "/" character. That makes a difference when you're dealing with URLs that have multiple layers. An example of that would be "post/1." That's the URL we're contending with when we hit "post/1." Altering our "assets" in our "master.blade.php" page will take care of that.
As an aside, depending on the language you're using, extends, inheriting and implementing are all words that apply to the same kind of thing in that you're allowing a class (or aspects of that class) to be replicated in different ways throughout your application. You can read more about extends in that way it's used in PHP by clicking here. Bottom line: The class you're extending is like a parent and the code you're extending it to becomes like a child. You're "extending" the functionality of parent to the child.
Look at the highlighted "@yield('content');" element. That's the way you're telling your "master.blade.php" page to be looking for a section called "content" in any page that constitutes and extension of the "master.blade.php" D) Asset Helper (back to top...) All your assets now are currently referenced as relative URLs. You can change them into Absolute URLs by using the Laravel "asset helper." To start with, you'll do this in your "master.blade.php" page: <script src="{{asset ('assets/vendor/jquery/jquery.min.js')}}"></script>< ...and then for all of your images, you'll do this: <header class="masthead" style="background-image: url('{{asset ('assets/img/post-bg.jpg')}}"> Notice that you still have to include the "assets" directory in the URL that pertains to both your JS, CSS and image files. E) Authentication (back to top...) Start by setting up a database using PhpMyAdmin and then adjusting the login criteria in your ".env" file: DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=personal-blog DB_USERNAME=root DB_PASSWORD= Next, head out to your "AppServiceProvider.php" page and make the following changes:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Schema::defaultStringLength(191); } }
After that, using the terminal, type in...
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:auth
This will create your migration. To run it, do this:
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan migrate
That will create your tables! In addition, it will set up your Authentication Controllers, Middleware and Views! It's beautiful! E) Authentication (back to top...) 1) mailtrap.io (back to top...) We're using mailtrap for the sake of establishing an email paradigm that we can use to test the whole resetting of one's password. F) Navigation (back to top...) 1) navigation.blade.php (back to top...) Grab the navigation content from "master.blade.php" and paste it into a new file called "navigation.blade.php" and put that in a new directory called "includes" within your "views" folder. After that, put this code in your "master.blade.php" page: @include('includes.navigation') 2) web.php (back to top...) Give each of your links a name in your "web.php" page:
Route::get('/', 'PublicController@index')->name('index'); Route::get('post/{id}', 'PublicController@singlePost')->name('singlePost'); Route::get('about', 'PublicController@about')->name('about'); Route::get('contact', 'PublicController@contact')->name('contact'); Route::post('contact', 'PublicController@contactPost')->name('contactPost'); Auth::routes(); Route::get('/home', 'HomeController@index')->name('home');
3) Authenticated Links (back to top...) To create a situation where some links are visible only when a particular user has been authenticated, here's what you do on the "navigation.blade.php" page:
@if(Auth::check()) <li class="nav-item"> <a class="nav-link" href="{{ route('dashboard') }}">Dashboard</a> </li> <li class="nav-item"> <form method="Post" id="logout-form" action="{{ route('logout') }}">@csrf</form> <a href="#" class="nav-link" onclick="document.getElementById('logout-form').submit();">Logout</a> </li> @else <li class="nav-item"> <a class="nav-link" href="{{ route('login') }}">Login</a> </li> <li class="nav-item"> <a class="nav-link" href="{{ route('register') }}">Register</a> </li> @endif
After you get that done, you're going to have to change the way certain pages reference "/home" in their code. You'll do that with: ResetPasswordController.php RegisterController.php RedirectIfAuthenticated.php LoginController.php BTW: Don't edit anything in the "vendor" folder. And that will do it! G) Admin Panel Assets (back to top...) We're going to use a Bootstrap template to create what will amount to a very nice looking admin suite. We're going out to startbootstrap.com to download a zip file. Once it's downloaded, you're going to copy the "assets" folder that's located in the "src" directory and paste that into a new folder in your application.
I had to use a different template. I went out to athemes.com
Under "public" create a new folder called "admin" and in that folder, place the "assets" folder you copied a moment ago. You're poised on the threshold of great things! 1) AdminController (back to top...) Create a new Controller that will facilitate all of the admin views that we're going to need.
php artisan make:controller AdminController
Now create a "dashboard" method that will direct the user to the "dashboard" blade. class AdminController extends Controller { public function dashboard() { return view('admin.dashboard'); } } 2) Admin Route (back to top...) In your "web.php" page, do this: Route::prefix('admin')->group(function() { Route::get('/dashboard', 'AdminController@dashboard')->name('adminDashboard'); }); That gets your "dashboard" concept into place. Now let's create that view... 3) Dashboard Blade (back to top...) Under your "views" directory, create another folder called "admin." This is where you're going to store all of your admin views. Once you've done that, create a new file called, "dashboard.blade.php." For now now just write the word, "Hi." Now, when you go out to http://personal.blog/admin/dashboard, you'll see your page. H) Protecting Admin Panel (back to top...) 1) Create, Register and Run CheckRole Middleware (back to top...) a) Create CheckRole (back to top...) We're going to start by creating a new Middleware called "CheckRole."
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:middleware CheckRole
b) Register CheckRole (back to top...) After you've done that, you have to register it in the "Kernel.php" file which is located in the "http" directory. That's going to look like this:
protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'checkRole' => \App\Http\Middleware\CheckRole::class, ];
BTW: Kernel is the central part of an application's operating system. With Middleware, generally you want it to fire with every request. So, you need to register it in the "kernel.php" file. c) Run CheckRole (back to top...) Add this to your "AdminController" page to ensure that the "CheckRole" middleware is running... public function __construct() { $this->middleware('checkRole: admin'); } 2) Edit CheckRole Middleware (back to top...) When you created "CheckRole," that put a file in your "http->Middleware" folder. The file looks like this:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Auth; class CheckRole { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next, $role) { if(Auth::check() == false || Auth::user()->admin != true) { return redirect('/'); } else { return $next($request); } } }
You need to make sure you're bringing the "Auth" package into the mix since you're referring to that as part of your "CheckRole" class. FYI: "Illuminate" is part of how Laravel names its components. Click here for more information. checking first to see if the user is logged in at all. Now looking to see if the user has "admin" permissions. The "admin" value is part of the "users" table that we added when we introduced the "auth" dynamic that we created with php artisan auth. I so dig that! I) Login Page (back to top...) You're downloading a free Bootstrap admin template that gives you some great features and asthetics right out of the box. Start by going out to StartAdmin-Free-Bootstrap and download the zip file. After you've downloaded it, you're going to retrieve the contents of the "assets" directory and install all those in a new folder you're going to create in the "public" folder of your project. Create another folder called "admin" and in that directory, create another folder called "assets" and put the content of the "assets" folder you just downloaded and put all of that content in the "assets" folder you just made. Once that's done, now you're going to the log "login.html" file in "src->demo_1->pages->samples and get all of the aesthetic settings. It will look like this - you're getting the higlighted stuff...
<!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>Star Admin Premium Bootstrap Admin Dashboard Template</title> <!-- plugins:css --> <link rel="stylesheet" href="../../../assets/vendors/iconfonts/mdi/css/materialdesignicons.min.css"> <link rel="stylesheet" href="../../../assets/vendors/iconfonts/ionicons/css/ionicons.css"> <link rel="stylesheet" href="../../../assets/vendors/iconfonts/typicons/src/font/typicons.css"> <link rel="stylesheet" href="../../../assets/vendors/iconfonts/flag-icon-css/css/flag-icon.min.css"> <link rel="stylesheet" href="../../../assets/vendors/css/vendor.bundle.base.css"> <link rel="stylesheet" href="../../../assets/vendors/css/vendor.bundle.addons.css"> <!-- endinject --> <!-- plugin css for this page --> <!-- End plugin css for this page --> <!-- inject:css --> <link rel="stylesheet" href="../../../assets/css/shared/style.css"> <!-- endinject --> <link rel="shortcut icon" href="../../../assets/images/favicon.png" /> </head> <body> <div class="container-scroller"> <div class="container-fluid page-body-wrapper full-page-wrapper"> <div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one"> <div class="row w-100"> <div class="col-lg-4 mx-auto"> <div class="auto-form-wrapper"> <form action="#"> <div class="form-group"> <label class="label">Username</label> <div class="input-group"> <input type="text" class="form-control" placeholder="Username"> <div class="input-group-append"> <span class="input-group-text"> <i class="mdi mdi-check-circle-outline"></i> </span> </div> </div> </div> <div class="form-group"> <label class="label">Password</label> <div class="input-group"> <input type="password" class="form-control" placeholder="*********"> <div class="input-group-append"> <span class="input-group-text"> <i class="mdi mdi-check-circle-outline"></i> </span> </div> </div> </div> <div class="form-group"> <button class="btn btn-primary submit-btn btn-block">Login</button> </div> <div class="form-group d-flex justify-content-between"> <div class="form-check form-check-flat mt-0"> <label class="form-check-label"> <input type="checkbox" class="form-check-input" checked> Keep me signed in </label> </div> <a href="#" class="text-small forgot-password text-black">Forgot Password</a> </div> <div class="form-group"> <button class="btn btn-block g-login"> <img class="mr-3" src="../../../assets/images/file-icons/icon-google.svg" alt="">Log in with Google</button> </div> <div class="text-block text-center my-3"> <span class="text-small font-weight-semibold">Not a member ?</span> <a href="register.html" class="text-black text-small">Create new account</a> </div> </form> </div> <ul class="auth-footer"> <li> <a href="#">Conditions</a> </li> <li> <a href="#">Help</a> </li> <li> <a href="#">Terms</a> </li> </ul> <p class="footer-text text-center">copyright © 2018 Bootstrapdash. All rights reserved.</p> </div> </div> </div> <!-- content-wrapper ends --> </div> <!-- page-body-wrapper ends --> </div> <!-- container-scroller --> <!-- plugins:js --> <script src="../../../assets/vendors/js/vendor.bundle.base.js"></script> <script src="../../../assets/vendors/js/vendor.bundle.addons.js"></script> <!-- endinject --> <!-- inject:js --> <script src="../../../assets/js/shared/off-canvas.js"></script> <script src="../../../assets/js/shared/misc.js"></script> <!-- endinject --> </body> </html>
What's happening is that that you're grabbing all of the aesthetic bells and whistles and then just dropping into the section that's not highlighted your original form that now has some new CSS attached to it. 1) auth.blade.php (back to top...) What you're going to do now is take all of that highlighed content and drop it into a new file that will go into your "layouts" directory. We're going to call it "auth.blade.php" and it will look like this:
<!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>@yield('title')</title> <!-- plugins:css --> <link rel="stylesheet" href="{{asset ('admin/assets/vendors/iconfonts/mdi/css/materialdesignicons.min.css')}}"> <link rel="stylesheet" href="{{ asset('admin/assets/vendors/iconfonts/ionicons/css/ionicons.css') }}"> <link rel="stylesheet" href="{{ asset('admin/assets/vendors/iconfonts/typicons/src/font/typicons.css') }}"> <link rel="stylesheet" href="{{ asset('admin/assets/vendors/iconfonts/flag-icon-css/css/flag-icon.min.css') }}"> <link rel="stylesheet" href="{{ asset('admin/assets/vendors/css/vendor.bundle.base.css') }}"> <link rel="stylesheet" href="{{ asset('admin/assets/vendors/css/vendor.bundle.addons.css') }}"> <!-- endinject --> <!-- plugin css for this page --> <!-- End plugin css for this page --> <!-- inject:css --> <link rel="stylesheet" href="{{ asset ('admin/assets/css/shared/style.css') }}"> <!-- endinject --> <link rel="shortcut icon" href="{{ asset('admin/assets/images/favicon.png') }}" /> </head> <body> <div class="container-scroller"> <div class="container-fluid page-body-wrapper full-page-wrapper"> <div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one"> @yield('content') <!-- content-wrapper ends --> </div> <!-- page-body-wrapper ends --> </div> <!-- container-scroller --> <!-- plugins:js --> <script src="{{ asset ('admin/assets/vendors/js/vendor.bundle.base.js') }}"></script> <script src="{{ asset ('admin/assets/vendors/js/vendor.bundle.addons.js') }}"></script> <!-- endinject --> <!-- inject:js --> <script src="{{ asset ('admin/assets/js/shared/off-canvas.js') }}"></script> <!--<script src="{{ asset('admin/assets/js/shared/misc.js') }}"></script>--> <!-- endinject --> </body> </html>
The @yield('content') is the darling piece of this code in that it is telling the system to "yield" to the code that's going to be coming into the page that we're calling "content." Check it out: 2) login.blade.php (back to top...)
@extends('layouts.auth') @section('title') Login @endsection @section('content') <div class="row w-100"> <div class="col-lg-4 mx-auto"> <div class="auto-form-wrapper"> <form method="POST" action="{{ route('login') }}"> @csrf <div class="form-group"> <label class="label">Email</label> <div class="input-group"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus style="width:95%;"> @error('email') <span class="invalid-feedback" role="alert" <strong>{{ $message }}</strong><br><br> </span> @enderror </div><br> </div> <div class="form-group"> <label class="label">Password</label> <div class="input-group"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password" placeholder="*********"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div><br> </div> <div class="form-group"> <button class="btn btn-primary submit-btn btn-block">Login</button> </div> </form> <div class="form-group d-flex justify-content-between"> <div class="form-check form-check-flat mt-0"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> <a href="{{ route('password.request') }}" class="text-small forgot-password text-black">Forgot Password</a> </div> <div class="text-block text-center my-3"> <span class="text-small font-weight-semibold">Not a member ?</span> <a href="{{ route('register') }}" class="text-black text-small">Create new account</a> </div> </div> </div> </div> @endsection
Notice how we're calling our login html and code content "content." That's what's going to allow the "auth.blade.php" file to correctly accept and display the supplementary code coming from this file.
You will have to make some CSS changes in order to get everything looking like your downloaded Bootstrap template. One thing you'll want to remember is how to get your input fields looking good. For that, I had to go into style.css in public/admin/assets/css/shared to adjust right hand border of fields. Another thing I had to do was add the highlighted <br> tag in order to create enough roo
J) Dashboard (back to top...) Grab the "index.html" from your Bootstrap template and put that in a new file in your "layouts" folder. Call it "admin.blade.php." After you do that, you'll want to find where the main content is going to be located and put, " @yield('content')" at that point. Now, go to your "views" directory and grab your "home.blade.php" file and rename it to "dashboard.blade.php." Once you do that, you'll want to adjust your "HomeController.php" file so it's referring to "/dashboard" rather than "/home." Also, change the layout you're referring to at the top of the page to "@extends('layouts.admin')." And that will do it! K) Dashboard Navigation (back to top...) First thing is get rid of the two <li> elements that form the email and notification icons / pulldown menu. 1) Login Name (back to top...) Next, you're going to replace the static "name" with the name of the hardcharger that's logged in with this: <p class="mb-1 mt-3 font-weight-semibold">{{ Auth::user()->name }}</p> 2) Sidebar Links (back to top...) You're going to change up the sidebar somewhat with a different class for you links as well as some additional icons. The class you're going to use is this: a.new_menu_link { color:#ffffff; align-items:center; padding:15px 30px 15px 25px; whitespace: nowrap; height:52px; font-size: 15px; font-weight:bold; font-weight:500; display:inline-block; display:flex; } This is on your "sidebar.css" page. Your links are going to look like this (with your icon): <li class="nav-item"> <a href="index.html" class="new_menu_link"> <ion-icon name="md-document" lazy="" role="img" class="hydrated" aria-label="person" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Courses</span> </a> </li> Notice the new class! And, for your icons, you're going to need a new JS code. You'll put that on your "admin.blade.php" page... <script src="{{ asset('admin/assets/vendors/iconfonts/ionicons/ionicons.js') }}"></script> And for the actual icons, you can click here for a reference. Your finished sidebar menu looks like this:
<ul class="nav"> <li class="nav-item nav-profile"> <a href="#" class="nav-link"> <div class="profile-image"> <img class="img-xs rounded-circle" src="../admin/assets/images/faces/face8.jpg" alt="profile image"> <div class="dot-indicator bg-success"></div> </div> <div class="text-wrapper"> <p class="profile-name">{{ Auth::user()->name }}</p> <p class="designation">Premium user</p> </div> </a> </li> <li class="nav-item nav-category">Main Menu</li> <li class="nav-item"> <a href="index.html" class="new_menu_link"> <ion-icon name="md-speedometer" lazy="" role="img" class="hydrated" aria-label="speedometer" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Dashboard</span> </a> </li> <li class="nav-item"> <a href="index.html" class="new_menu_link"> <ion-icon name="md-person" lazy="" role="img" class="hydrated" aria-label="person" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Customers</span> </a> </li> <li class="nav-item"> <a href="index.html" class="new_menu_link"> <ion-icon name="md-document" lazy="" role="img" class="hydrated" aria-label="person" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Courses</span> </a> </li> <li class="nav-item"> <a href="index.html" class="new_menu_link"> <ion-icon name="md-trophy" lazy="" role="img" class="hydrated" aria-label="person" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Quizzes</span> </a> </li> <li class="nav-item"> <a class="new_menu_link" data-toggle="collapse" href="#auth" aria-expanded="false" aria-controls="auth"> <ion-icon name="ios-videocam" lazy="" role="img" class="hydrated" aria-label="person" style=font-size:18pt;"></ion-icon>   <span class="menu-title">Videos</span> <i class="menu-arrow"></i> </a> <div class="collapse" id="auth"> <ul class="nav flex-column sub-menu"> <li class="nav-item"> <a class="nav-link" href="pages/samples/blank-page.html"> Blank Page </a> </li> <li class="nav-item"> <a class="nav-link" href="pages/samples/login.html"> Login </a> </li> <li class="nav-item"> <a class="nav-link" href="pages/samples/register.html"> Register </a> </li> <li class="nav-item"> <a class="nav-link" href="pages/samples/error-404.html"> 404 </a> </li> <li class="nav-item"> <a class="nav-link" href="pages/samples/error-500.html"> 500 </a> </li> </ul> </div> </li> </ul>
The highlighted section represents a very cool pulldown that I want to keep in mind... 3) Sidebar Links (back to top...) You're going to copy and paste the logout code from your "navigation.blade.php" page. The finished logout code on your "header.navigation.blade.php" page will look like this: <form method="Post" id="logout-form" action="{{ route('logout') }}">@csrf</form> <a class="dropdown-item"onclick="document.getElementById('logout-form').submit();" style="cursor:pointer;">Sign Out<i class="dropdown-item-icon ti-power-off"></i></a> </form> L) Creating, Migrating, Seeding...Posts and Comments Table (back to top...) Several commands, most of which we've already gone over, but here's some of what happened...
brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:migration create_posts_table --create=posts Created Migration: 2019_08_13_013432_create_posts_table brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan migrate Migrating: 2019_08_13_013432_create_posts_table Migrated: 2019_08_13_013432_create_posts_table brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php make:model Post Could not open input file: make:model brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:model Post Model created successfully. brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan make:seeder PostSeeder Seeder created successfully. brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ composer dump-autoload Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: beyondcode/laravel-dump-server Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. Generated optimized autoload files containing 3630 classes brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog $ php artisan db:seed --class=PostSeeder Database seeding completed successfully. brucegust@BRUCEGUST59AC MINGW64 /c/wamp/www/personal-blog
M) Displaying Posts & Comments (back to top...) 1) Dynamic Title (back to top...) In your "header," you'll want to change it to this: <title>@yield('title', 'Laravel Blog')</title> That tells Laravel to "yield" to whatever the "title" variable may be and print that. If there's no "title" variable, then write "Laravel Blog." 2) welcome.blade.php / Post.php / Public Controller (back to top...) Here's what your "welcome.blade.php" page will look like:
<div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> @foreach($posts as $post)
<div class="post-preview"> <a href="#"> <h2 class="post-title"> {{$post->title }} </h2> <h3 class="post-subtitle"> Problems look mighty small from 150 miles up </h3> </a> <p class="post-meta">Posted by <a href="#">{{$post->user->name}}</a>
on {{ date_format($post->created_at, 'F d, Y') }} | <i class="fa fa-comment" aria-hidden="true"></i> {{ $post->comments->count() }}
</p> </div> <hr> @endforeach <!-- Pager --> <div class="clearfix"> <a class="btn btn-primary float-right" href="#">Older Posts →</a> </div> </div> </div> </div> @endsection
Now, a lot of this isn't going to make sense without the "Post.php" Model and the Public Controller, so let's bring those puppies to the screen. You'll see things color coded so as to reference what code goes with what function etc. Posts Model
class Post extends Model {
public function user() { return $this->belongsTo('App\user'); }
public function comments() { return $this->hasMany('App\Comment'); } }
Public Controller
<?php namespace App\Http\Controllers;
use App\Post; use Illuminate\Http\Request; class PublicController extends Controller { public function index() { $posts = Post::all();
return view('welcome', compact ('posts')); } public function singlePost($id) { return view('singlePost'); } public function about() { return view('about'); } public function contact() { return view('contact'); } public function contactPost() { } }
the "foreach" dynamic is going to loop through all of the "posts" belonging to this user. The blue square is the symbol that shows which part of the Model and the Controller go with this particular syntax. You'll notice in the "Public Controller," you've got this: $posts = Post::all(); return view('welcome', compact ('posts')); The highlighted part is what was added. You've got the first part which amounts to a "select all" and then you're passing that array into your "welcome" view with the "compact('posts')" script. Nice! {{$post->title }} works because "post" is your variable that you set up in the "foreach" statement and you're grabbing the "title" part of that array which is coming from your "posts" table. {{$post->user->name}} works because of the JOIN / relationship you set up in the "Post.php" model. You can see it to the right of the red square. This... public function user() { return $this->belongsTo('App\user'); } ...sets up an INNER JOIN between the "posts" table and the "users" table. That's how you can now access the user that authored every post. on {{ date_format($post->created_at, 'F d, Y') }} - that's the date coming from the "posts" table combined with the necessary syntax that coverts the date to something pretty. this syntax may be starting to look familiar because of the way it's structured. Sure enough, you've got another relationship happening here which was set up in the Model: public function comments() { return $this->hasMany('App\Comment'); } ...and that's why {{ $post->comments->count() }} works. N) Displaying Single Post & Comments (back to top...) First, you've got your route... Route::get('post/{id}', 'PublicController@singlePost')->name('singlePost'); Then you've got your Controller: public function singlePost($id) { $post = Post::find($id); return view('singlePost', compact('post')); } You're grabbing the post that matches the $id coming from the "find" function and then passing that array of "stuff" into the view. This is your "singlePost.blade.php:"
<header class="masthead" style="background-image: url('{{asset ('assets/img/post-bg.jpg')}}"> <div class="overlay"></div> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div class="post-heading"> <h1>{{ $post->title }}</h1> <span class="meta">Posted by <a href="#">{{ $post->user->name }}</a> on {{ date_format($post->created_at, 'F d, Y') }} </span> </div> </div> </div> </div> </header>
A) Setting Up the Project and Shortcuts (back to top...) 1) composer create-project (back to top...) This is going to be a whirlwind tour based on two videos. The first one shows you how to set up an API in Laravel. The second shows how to take all that knowledge and incorporate Vue.JS so you can build a single page application. Here's the way you're going to set up your API. Navigate to the directory you want to install your new project. So, if you're in your root directory and you plan on having a project in a "new_project" folder, then you're exactly where you need to be if you're in your root directory. Don't create another folder named, "new_project." Otherwise, you're going to have a folder in your "new_project" directory called, "new_project." Once you're good to go on that score, enter this on Git Bash:
composer create-project --prefer-dist laravel/laravel pages.site
BTW: --prefer -dist is, "...--prefer-dist: Reverse of --prefer-source, composer will install from dist if possible. This can speed up installs substantially on build servers and other use cases where you typically do not run updates of the vendors. It is also a way to circumvent problems with git if you do not have a proper setup." Basically, it's going to let you speed up installation. This sets up all your packages, libraries and dependencies. After that, set up your shortcuts based on the "clunky" URL of "localhost/pages/public/index," You can review those steps by clicking here.
BTW: If you're cloning a project from Github, remember that "composer install" is to Laravel what "npm install" is to Node. You've got to run that command to set up your "vendor" directory. Otherwise, you'll get an error that refers to your "autoload" script in your index file which means nothing if you don't know what it's referring to!
2) database / .env file (back to top...) Setup your database in phpMyAdmin. In this case, we're going to call "pages_database" and set things up accordingly in the ".env" file. DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=pages_database DB_USERNAME=root DB_PASSWORD= 3) migration (back to top...) You'll be using "migrations" and "seeds" to create and populate your tables so that's all you have to do at this step. i) create migration file / adjust AppServiceProvider (back to top...)
php artisan make:migration create_articles_table --create-pages_database
When you create that file, you've created, not only the syntax that will create your table, but you'll also create what another developer will need in order to mimic your set up if that becomes an issue. After you've done that, you'll see the file you just created in app -> database -> migrations. It's just "scaffolding," though. You've got to add the fields that are specific to your app. In this case, it looks like this:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateArticlesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('articles', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->text('body'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('articles'); } }
What was added in seen above in bold. To start off with, go to app -> Providers and open up "AppServiceProvider.php." Add the syntax you see in bold below:
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; //use Illuminate\Http\Resources\Json\Resource; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Schema::defaultStringLength(191); //Resource::withoutWrapping(); } }
That prevents the "length" error from occuring. ii) create seeder file (back to top...) To create your "seeder" file, do this:
php artisan make:seeder ArticlesTableSeeder
After running that, you get the "ArticlesTableSeeder.php" file in the "app -> seeds" directory:
<?php use Illuminate\Database\Seeder; class ArticlesTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { factory(App\Article::class, 30)->create(); } }
What this is doing is specifying the Model, which is going to be in your "app" directory. The Model hasn't been made yet, but we're getting there. When it is put in place, this "seeder" will be pointing to it. The highlighted section is something you may have to be aware of because it references it as a "Model" right out of the chute. iii) Adjust DatabaseSeeder.php File (back to top...) Go to your "DatabaseSeeder.php" file in your app -> database -> seeds directory and adjust it according to what you see in bold below.
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(ArticlesTableSeeder::class); } }
iv) Make Article Factory (back to top...) Thus far you've......now, you're going to create your "factory."
php artisan make:factory ArticleFactory
Go to app -> database -> factories to see the file you just created. You'll see something like this (the code's been altered according to what you see in bold below);
<?php /** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\Model; use Faker\Generator as Faker; $factory->define(App\Article::class, function (Faker $faker) { return [ 'title' => $faker->text(50), 'body' => $faker->text(200) ]; });
v) Make Model (back to top...) Time to make our Model. To do that, you'll run this code:
php artisan make:model Article
That gives you this file:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { // }
vi) Create Tables (back to top...) In one fell swoop, we're going to create all of our tables by doing this:
php artisan migrate
That makes all of our tables. The next step is to populate them with some data... vii) Add Dummy Data (back to top...) Now, we're going to create some dummy data in our "articles" table.
php artisan db:seed
What this is going to do is run any "seeder" file you've got in your "seeds" directory. Notice the convention when it comes to naming tables. In this case, you're populating a table called, "articles." In our "migration" file, you're going to refer to that table as "article..." factory(App\Article::class, 30)->create(); That gives us 30 rows of dummy data that we can now use as a way to test the functionality we're going to create to administer our site's content. B) Setting Up Controllers and Routes (back to top...) 1) List Articles (back to top...) i) ArticlesController.php (back to top...) Start by making your Controller that's going to "control" your "articles" table:
php artisan make:controller ArticleController --resource
By adding "resource," it will automatically generate placeholders for your main blocks of functionality. Here's your finished Controller:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Article; use App\Http\Resources\Article as ArticleResource; class ArticleController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { //get articles $articles = Article::paginate(15); // "Article" is your model. "paginate" tells the system to display the rows 15 at a time //return collection of articles as a resource return ArticleResource::collection($articles); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // $article = $request->isMethod('put') ? Article::findOrFail ($request->article_id) : new Article; $article->id = $request->input('article_id'); $article->title = $request->input('title'); $article->body = $request->input('body'); if($article->save()) { return new ArticleResource($article); } } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { //get article $article = Article::findOrFail($id); //return single article as resource return new ArticleResource($article); } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { $article = Article::findOrFail($id); if($article->delete()) { return new ArticleResource($article); } } }
you're going to need this as part of being able to process requests coming from headers etc. Establish a variable where you can store your list of rows coming from the "Article" model. You'll add some additional syntax that give you the automatic pagination simply by invoking that one command! here's where you're going to use a Laravel feature called "Resource," which makes for a very streamlined approach to retrieving a list of rows from a table in a JSON format. Here's the definition as it's coming from the Laravel docs:
When building an API, you may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users. Laravel's resource classes allow you to expressively and easily transform your models and model collections into JSON.
In this case, you're working with the "Article" model. You define your "dependency" at the top of the page and name it "ArticlesResource" and then invoke it in your code and pass the "article" variable into it via the paranthesis. ii) api.php (routes) (back to top...)
<?php use Illuminate\Http\Request; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); //list articles Route::get('/articles', 'ArticleController@index'); //list single article Route::get('/article/{id}', 'ArticleController@show'); //create new article Route::post('/article', 'ArticleController@store'); //update article Route::put('/article', 'ArticleController@store'); //delete an article Route::delete('/article/{id}', 'ArticleController@destroy');
iii) Resource (back to top...) This was already mentioned in the "ArticleController" section and, from that perspective, it's out of order. With the RESTful dynamic, you want something that serves as a liason between your Model and your Controller so you're able to retrieve your data in a JSON format. You're going to actually add this to your app with this command:
php artisan make: resource Article
That's the file that you see in the, "http->Resources" directory which looks like this:
<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class Article extends JsonResource { /** * Transform the resource into an array. * * @param \Illuminate\Http\Request $request * @return array */ public function toArray($request) { //return parent::toArray($request); return [ 'id' => $this->id, 'title' => $this->title, 'body' => $this->body ]; } public function with($request) { return [ 'version' => '1.0.0', 'author_url' => url('http://brucegust.com') ]; } }
this is the only real "edit." This is all straight out of the box in that return parent::toArray($request); gives you an array of the entire column set. This piece of syntax: return [ 'id' => $this->id, 'title' => $this->title, 'body' => $this->body ]; ...is what gives you the opportunity to specify what column names you want to receive from the database. iv) Postman / API in Action (back to top...) Let's use Postman and see this in action! In Postman, we're going to do this: pages.site/api/articles That's going to trigger the route, Route::get('/articles', 'ArticleController@index'); and that's going to put in motion this guy, right here: public function index() { //get articles $articles = Article::paginate(15); //return collection of articles as a resource return ArticleResource::collection($articles); } See the above, "exploded" view of the Controller to see all of the syntax explained in more detail. Because of the pagination, we get 15 rows as well as the syntax we need to have in place to trigger the page that will give us the next 15 rows! See the highlighted section below.
{ "data": [ { "id": 1, "title": "Ad magnam rerum eaque. Laborum est ipsa labore.", "body": "Facilis illum vel qui odio laudantium et doloremque alias. Nisi eum ipsum quibusdam ut. Sed pariatur accusamus facilis similique quis iusto." }, { "id": 31, "title": "Test Title", "body": "test body" }, { "id": 32, "title": "Test Title", "body": "test body" }, { "id": 33, "title": "Test Title", "body": "test body" }, { "id": 3, "title": "Dolores ut ut animi quia.", "body": "Aliquid autem eum quidem perferendis deleniti corporis et magnam. Laborum et beatae aut doloremque nulla consequatur qui. Tempore sit ut culpa ut voluptatem quod repudiandae nam." }, { "id": 4, "title": "Updated Tite", "body": "updated body" }, { "id": 5, "title": "Odio architecto cum quae illum fuga odio quis.", "body": "Vero nulla ullam quisquam odio neque consequatur. Ut sed sint eum ea recusandae unde aliquid. Sit sed occaecati et id ea." }, { "id": 6, "title": "Vel ut ut nobis cum consequatur voluptatibus.", "body": "Et ea assumenda ipsa voluptatum ab quia qui. Alias accusamus eos ratione quidem ab rerum quis. Quis maxime qui fuga sapiente facilis architecto impedit. Architecto amet magni quia voluptate qui." }, { "id": 7, "title": "Officia ipsum enim et.", "body": "Voluptates ut enim voluptatem qui omnis repellat qui. Tempore beatae qui dolorem et totam sed autem sunt. Excepturi cumque possimus est eum autem." }, { "id": 8, "title": "Illum incidunt qui voluptates natus et.", "body": "Ad provident velit debitis esse. Quas voluptatem optio molestiae placeat a dolorem. Dolorem fuga quas natus aut voluptatem qui dolor." }, { "id": 9, "title": "Quasi aliquid voluptas qui repudiandae.", "body": "Cumque doloribus impedit sed aut omnis. Molestiae et et vel officia neque itaque. In ducimus illo laborum aut hic ut incidunt." }, { "id": 10, "title": "Odio fugiat et asperiores autem.", "body": "Vitae odit numquam facilis ipsam illum eum. Eum quo tempora excepturi. Voluptates facere quidem aliquam pariatur asperiores." }, { "id": 11, "title": "Enim sapiente ea et.", "body": "Modi dolor excepturi voluptate est omnis. Eum explicabo dolorum officiis adipisci. At cumque aspernatur distinctio dolorum modi est. Et tempora illum voluptatibus autem dolores." }, { "id": 12, "title": "Vel deserunt sint non sit consequatur.", "body": "Dolor accusamus architecto omnis. Earum dolor temporibus expedita odio eos porro. Ratione modi rerum maxime." }, { "id": 13, "title": "Amet et recusandae expedita.", "body": "Dolor odit accusamus omnis dignissimos pariatur alias qui. Omnis rem quia necessitatibus atque. Dolorum natus et atque magni consectetur asperiores veritatis. Molestiae aut id aut." } ], "links": { "first": "http://pages.site/api/articles?page=1", "last": "http://pages.site/api/articles?page=3", "prev": null, "next": "http://pages.site/api/articles?page=2" }, "meta": { "current_page": 1, "from": 1, "last_page": 3, "path": "http://pages.site/api/articles", "per_page": 15, "to": 15, "total": 32 } }
2) Display Article (back to top...) To display a single article, you're using this code in your Controller:
public function show($id) { //get article $article = Article::findOrFail($id); //return single article as resource return new ArticleResource($article); }
your $id is coming from your route: Route::get('/article/{id}', 'ArticleController@show'); "findOrFail..."
Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The findOrFail and firstOrFail methods will retrieve the first result of the query; however, if no result is found, a Illuminate\Database\Eloquent\ModelNotFoundException will be thrown. (Laravel Docs)
this is yet another use of your "resource" that Eloquent uses for API dynamics When you go to Postman and do a "POST" using http://pages.site/api/article/4, you get this:
{ "data": { "id": 4, "title": "Updated Tite", "body": "updated body" }, "version": "1.0.0", "author_url": "http://brucegust.com" }
Notice how your object is wrapped in a "data" key. If you want to remove that part of what's being returned, you got to AppServiceProvider.php in your Providers directory and add what's highlighted below.
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; use Illuminate\Http\Resources\Json\Resource; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Schema::defaultStringLength(191); Resource::withoutWrapping(); } }
When you do that, you get this:
{ "id": 4, "title": "Updated Tite", "body": "updated body" }
Notice now you don't get the info wrapped up in a "data" object. You can also add information to what you're getting back from the database by altering your Resource Article.php file like this:
public function with($request) { return [ 'version' => '1.0.0', 'author_url' => url('http://brucegust.com') ]; }
Now, you'll get this from your "display" code: { "data": { "id": 4, "title": "Updated Tite", "body": "updated body" }, "version": "1.0.0", "author_url": "http://brucegust.com" } Bear in mind that when you do this, regardless of how you edited your "AppServiceProvider.php" page, you'll still get everything wrapped up in your "data" object. BTW: You can get into what represents a bit of standard for JSON API development. You can read all about it here. 3) Add / Edit Article (back to top...) To add or edit an article, you're going to use your "store" function on the "ArticleController.php" page.
public function store(Request $request) { // $article = $request->isMethod('put') ? Article::findOrFail($request->article_id) : new Article; $article->id = $request->input('article_id'); $article->title = $request->input('title'); $article->body = $request->input('body'); if($article->save()) { return new ArticleResource($article); } }
this is an IF statement that's asking whether or not your app is using the "PUT" method and, if so, it's invoking the "findOrFail" method. If that is the case, it's grabbing the "article_id" from the page. Real quick, here's a summary of the "ternary" IF construction:
Boolean Expression ? First Statement : Second Statement
As you can see in the above syntax, ternary operator includes three parts. First part (before ?) includes conditional expression that returns boolean value true or false. Second part (after ? and before :) contains a statement which will be returned if the conditional expression in the first part evalutes to true. The third part includes another statement which will be returned if the conditional expression returns false. Also, as a quick aside, Article is your "Article" model which you imported into your Controller with this line of code: use App\Article;
The difference between an object and an instance one more time: "A blueprint for a house design is like a class description. All the houses built from that blueprint are objects of that class. A given house is an instance."
So, in this one ternary IF statement you're saying that if the app is using a "PUT" dynamic, you're going to use the "findOrFail" method and passing the value of the article_id into the method. Otherwise, you're setting up an entirely new "Article" object. Having defined whether or not this is an entirely new entry or you're merely updating an existing record, once the user hits "save," you grab the incoming data and process it by passing your object into your new "resource" object. To update an article then, your Postman interface is going to look like this: You're going to use the PUT method and your URL is "http://pages.site/api/article." The system recognizes PUT and then looks for the id of the already existing record. In our Postman interface, we put "content type" of "appplication/json" in our header and then put row JSON code into our body that looked like this: { "article_id": 4, "title": "Updated Tite", "body": "updated body" } And that updated our article. To put a new article into the database, we used the same "application/json" content type and then this will be the raw JSON in your body: { "title": "Test Title", "body": "test body" } You'll use the URL, only this time you're using "POST" instead of "PUT" and that's all there is to it! 4) Delete Article (back to top...) To delete an article, you're going to start by coping the code you had in "show" and making a couple of modifications.
public function destroy($id) { $article = Article::findOrFail($id); if($article->delete()) { return new ArticleResource($article); } }
start by finding your article with "findOrFail." if the request is a "delete," then system knows to delete that from the database AND send back the information that you just thew in the trash can. Remember that your route needs to be this: Route::delete('/article/{id}', 'ArticleController@destroy'); You've got your "id" in the route and "article" is singluar. C) Vue.js (back to top...)

Laravel is currently shipping version 6. If you want to run Vue, then you've got to install it by hand and specifiy it's usage. Otherwise, you won't have the infrastructure or the functionality. Here's what you do: composer require laravel/ui --dev php artisan ui vue At that point, you'll get a prompt that will ask you to run npm install and npm run devThen...and only then... you are good to go!
This was a real mess at the beginning. Even after getting some apparent deficiencies addressed, anytime I went to run npm run watch, I got this error:
$ npm run watch > @ watch C:\wamp\www\larticles > cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js 'cross-env' is not recognized as an internal or external command, operable program or batch file. npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! @ watch: `cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the @ watch script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\brucegust\AppData\Roaming\npm-cache\_logs\2019-12-01T12_05_13_664Z-debug.log
In the end, it was a wild goose chase that was finally solved by running the above commands (composer require laravel/ui --dev | php artisan ui vue). Short of that, and it was a mess that never stopped. 1) Install Dependencies and "npm run watch" (back to top...) i) Bringing Files from One Project to Another (back to top...) When you're bringing files from one project to another, you'll copy and paste your Controllers and your Routes. One thing that might be a little illusive, however, is your Models. Those are sitting in your "app" directory and are absolutely essential in order for your Controller to work in the context of your database. ii) package.json (back to top...) You'll run npm install to get your localized app up to speed with all of the Node related dependencies in place. In your "package.json" file, you have this: "devDependencies": { "axios": "^0.19", "bootstrap-sass": "^3.3.7", "cross-env": "^5.1", "jquery": "^3.2", "laravel-mix": "^4.0.7", "lodash": "^4.17.13", "resolve-url-loader": "^2.3.1", "sass": "^1.15.2", "sass-loader": "^7.1.0", "vue-template-compiler": "^2.6.10", "vue": "^2.5.7" } A couple of things you'll want to be aware of...
This has been stated before but it's worth repeating: When you import a project from Github, not only will you have to run npm install, but you'll also have to run composer install. That's how you get your "vendor" directory and all of your dependencies beyond what's related specifically to JavaScript / Node etc. Click here for more info.
SASS stands for "syntactically awesome style sheets." It adds a layer of functionality on to your CSS script that makes it a far more verbose and functional approach to stylesheets. Click here for even more information. In this app, you've got a bunch of packages and dependencies that make for a great looking / functioning application. While Composer orchestrates and organizes PHP dependencies, Laravel Mix is organizing your Node tech. It's a layer of functionality positioned over the JS Webpack technology. You'll use npm run watch to keep track of any updates or changes to those technologies to ensure your app doesn't get hung up on an upgrade that it isn't prepared for. Here's what the Laravel docs say...
The npm run watch command will continue running in your terminal and watch all relevant files for changes. Webpack will then automatically recompile your assets when it detects a change...
Here's the syntax you need to remove all of your node packages and start fresh just in case you feel like you need to start at the beginning. rm -rf node_modules rm package-lock.json yarn.lock npm cache clear --force npm install
iii) composer.json (back to top...) This is similar to Node in that you've got several "packages" that are needed in order for this puppy to run. While npm install uploads all of your Node dynamics, composer install uploads all of your PHP tech. 2) Basic Files (back to top...) i) npm run watch (back to top...) "npm run watch" is a needed piece of tech in order to update your vue.js files. Reason being is that "vue" is a framework. There's a difference between a library and a framework...
The "^" character is a piece of syntax that tells your system to go ahead and update older versions of your various libraries. Click here for more information. "yarn" is simliar to "npm," but in some ways is a little more secure and effective. Click here for more info.
A library can be defined as bunch of code which can be used for a specific purpose. Typical example of a library is jQuery which is a JavaScript library consisting of API ’s through which one can easily manipulate HTML and CSS and also provide a better, dynamic outlook to your website. On the other hand, framework can be defined as the skeleton of a project which helps you eradicate the need of hard coding……. by this i mean to say that nothing needs to be done from scratch and it provides initial support to start the project. Bootstrap can be considered as a typical example of a web framework which provides you all the pre defined classes and fonts and many other features your which makes your development faster and easier (from quora.com).
That being the case, when you make changes to your vue.js files within Laravel, you're affecting more than just that one line of code. Rather, you're potentially affecting several files, hence the need for npm run watch ii) welcome.blade.php (back to top...)
<!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() }}"> <script>window.Laravel = { csrfToken: '{{ csrf_token() }}' }</script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <title>The Starting Line</title> <!-- Fonts --> <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet"> <!-- Styles --> </head> <body> <div id="app"> <navbar></navbar> <div class="container"> <articles></articles> </div> </div> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script> <script src="{{ asset('js/app.js') }}"></script> </body> </html>
CSRF stands for "Cross-Site Request Forgery." It's an incredibly clever way of maintaining your site's security. Here's a great example from stackoverflow.com.
Cross-Site Request Forgery (CSRF) in simple words:
  • Assume you are currently logged into your online banking at www.mybank.com
  • Assume a money transfer from mybank.com will result in a request of (conceptually) the form http://www.mybank.com/transfer?to=&l;SomeAccountnumber>;amount=<SomeAmount>. (Your account number is not needed, because it is implied by your login.)
  • You visit www.cute-cat-pictures.org, not knowing that it is a malicious site.
  • If the owner of that site knows the form of the above request (easy!) and correctly guesses you are logged into mybank.com (requires some luck!), they could include on their page a request like http://www.mybank.com/transfer?to=123456;amount=10000 (where 123456 is the number of their Cayman Islands account and 10000 is an amount that you previously thought you were glad to possess).
  • You retrieved that www.cute-cat-pictures.org page, so your browser will make that request.
  • Your bank cannot recognize this origin of the request: Your web browser will send the request along with your www.mybank.com cookie and it will look perfectly legitimate. There goes your money!
This is the world without CSRF tokens. Now for the better one with CSRF tokens:
  • The transfer request is extended with a third argument: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • That token is a huge, impossible-to-guess random number that mybank.com will include on their own web page when they serve it to you. It is different each time they serve any page to anybody.
  • The attacker is not able to guess the token, is not able to convince your web browser to surrender it (if the browser works correctly...), and so the attacker will not be able to create a valid request, because requests with the wrong token (or no token) will be refused by www.mybank.com.
  • Result: You keep your 10000 monetary units. I suggest you donate some of that to Wikipedia.
    Vue sometimes complains if this isn't in place. Bootstrap stuff this is where your "vue.js" files are being displayed this is the "app.js" file that's coming from your "js" directory. This is what's coordinating your Vue.js files iii) app.js (back to top...)
    /** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ require('./bootstrap'); window.Vue = require('vue'); /** * The following block of code may be used to automatically register your * Vue components. It will recursively scan this directory for the Vue * components and automatically register them with their "basename". * * Eg. ./components/ExampleComponent.vue -> <example-component></example-component> */ // const files = require.context('./', true, /\.vue$/i) // files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default)) Vue.component('navbar', require('./components/Navbar.vue').default); Vue.component('articles', require('./components/Articles.vue').default); /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ const app = new Vue({ el: '#app', });
    Vue provides an ES module that is used by webpack. "Webpack" is a module bundler which is a tool that takes pieces of JavaScript and their dependencies and bundles them into a single file, usually for use in the browser. Refer to the graphic to the right to get an idea of how it works and what it does. Bottom line: In JavaScript, if you're still manually including each JavaScript file you depend on with a script tag... there's a better way! ES6 introduced the concept of modules: the ability (finally!) to isolate our JavaScript into small components that live in different files (like we do with classes in JavaScript). But, to get this to work on the web, we need some help. We need something that's able to read our imports and aggregate everything we need into a single JavaScript file. We need webpack! (taken from symfonycasts.com). this is a Vue Component. You're associating it with the "navbar" element in the DOM (Data Object Model). this is coming from the Vue documentation...
    Provide the Vue instance an existing DOM element to mount on. It can be a CSS selector string or an actual HTMLElement. After the instance is mounted, the resolved element will be accessible as vm.$el.
    In other words, you have a new Vue instance (const app...) and with the "el" syntax you're saying you defining the element (el) that you want to mount your Vue instance on. BOOM! iv) Articles.vue (back to top...) Here's the first round of code that we built...
    <template> <div> <h2>Articles</h2> </div> </template> <script> export default { data() { return { articles: [], article: { id: '', title: '', body: '' }, article_id: '', pagination: {}, edit: false } }, created() { this.fetchArticles(); }, methods: { fetchArticles() { fetch('api/articles') .then(res => res.json()) .then(res=> { console.log(res.data); }) } } } </script>
    The <template> tag holds its content hidden from the client. Content inside a <template> tag will not be rendered. The content can be made visible and rendered later by using JavaScript. Taken from w3schools.com
    These tags make complete sense when you consider what it looks like when you're writing straight Vue.js code. Here's a portion of the tutorial on Vue.js in 60 Minutes: import Vue from 'vue' // import Vue import App from './App' // import your main "App" component from Vue new Vue({ // new Vue instance el: '#app', // this is the element that we're attaching the Vue instance to. In this case, the name of the element is "app." template: '<App/>', // the <template> tag is actually an HTML element. See callout to the right... components: { App } // a list of all the components you're going to foward to your <template> tag. Technically, this is where you're "registering your components }) export default is part of the ES6 version of JavaScript. It's basically saying that you want to export everything you're getting ready to program. data() is a property of a Vue instance that sets the properties of that Vue instance, and those properties can be accessed by and bound to the component. Here's an example that's coming from EE: <div id="myApp"> <h1>Hello {{ name }}</h1> </div> new Vue({ el : '#myApp', data : { name: 'Chris' } }); In the single instance example above, you'll see that the value of the data property is just an object (which has a single property called name). When you're building re-usable components, your data property needs to be a function that returns an object. This is so that each instance (copy) of your component has access to it's own copy of the data (otherwise changing a property value on one instance would change the value on all instances!) The syntax you've used above is just shorthand for the following: export default { data : function() { return { prop1 : "Some Value", prop2 : "Another Value", } }, As far as what you have after "return," here's a quick summary of objects and arrays as they're used in JavaScript: In Javascript you have Arrays and you have Objects. Generally speaking an Array is a collections of 'things', and an Object is a single 'thing' that contains related properties (for example a Customer object might have a FirstName property, and a LastName Property). An Array can contain a collection simple things (primitives), such as strings, numbers etc (the contents of an array don't have to be the same type!), but it can also contain a collection of Objects. An Object contains properties, which in turn can also be Arrays! Arrays use the square brackets and you access the elements of that array using the zero-based index: var myArray = ["Value1", "Value2", "Value3"]; console.log( myArray[0] ); // Value 1; Objects use the curly brackets and you access the properties using the period: var myObject = { FirstName : "Chris", LastName : "Stanyon" }; console.log( myObject.FirstName ); // Chris As I said, Arrays can contain Objects, and Object Properties can be Arrays! var customers = [ { FirstName : "Chris", LastName : "Stanyon", PhoneNumbers : [ "123456", "098876" ] }, { FirstName : "Bruce", LastName : "Gust", PhoneNumbers : [ "00000", "55555" ] }, ]; What we have here is an Array of Customer Objects. Each Customer has 3 Properties - FirstName, LastName and PhoneNumbers. PhoneNumbers is an Array. You can access the data like so: customers[0].FirstName; // FirstName property of the 1st customer customers[1].LastName; // LastName property of the 2nd customer customers[1].PhoneNumbers[0]; // 1st PhoneNumber of the 2nd customer This is coming from a Fitbit project that we worked on a while ago and you can see some more context by clicking here "created" is a "Lifecycle Hook. Basically, it's a label that indicates in what order is a particular piece of code going to fire. Click on the link for more information and / or refer to the diagram to the right to see what kind of role it plays in the overall scheme of things. pretty straight forward. This is your list of methods. In this case, we're grabbing the results from an api and retrieving them as JSON. The above code gives you what is coming from your api/artilces API which looks like this: {"data":[{"id":33,"title":"Test Title","body":"test body","created_at":"2019-11-10 02:21:19","updated_at":"2019-11-10 02:21:19"},{"id":31,"title":"Test Title","body":"test body","created_at":"2019-11-10 02:20:57","updated_at":"2019-11-10 02:20:57"},{"id":1,"title":"Ad magnam rerum eaque. Laborum est ipsa labore.","body":"Facilis illum vel qui odio laudantium et doloremque alias. Nisi eum ipsum quibusdam ut. Sed pariatur accusamus facilis similique quis iusto.","created_at":"2019-11-09 04:02:01","updated_at":"2019-11-09 04:02:01"},{"id":3,"title":"Dolores ut ut animi quia.","body":"Aliquid autem eum quidem perferendis deleniti corporis et magnam. Laborum et beatae aut doloremque nulla consequatur qui. Tempore sit ut culpa ut voluptatem quod repudiandae nam.","created_at":"2019-11-09 04:02:01","updated_at":"2019-11-09 04:02:01"},{"id":4,"title":"Updated Tite","body":"updated body","created_at":"2019-11-09 04:02:01","updated_at":"2019-11-10 02:39:31"}],"links":{"first":"http:\/\/larticles.site\/api\/articles?page=1","last":"http:\/\/larticles.site\/api\/articles?page=7","prev":null,"next":"http:\/\/larticles.site\/api\/articles?page=2"},"meta":{"current_page":1,"from":1,"last_page":7,"path":"http:\/\/larticles.site\/api\/articles","per_page":5,"to":5,"total":31}} 3) Cadillac Display (back to top...) Here's how we code this thing so it looks camera ready!
    <template> <div> <h2>Articles</h2> <nav aria-label="Page navigation example"> <ul class="pagination"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"> <a class="page-link" href="#" @click="fetchArticles(pagination.prev_page_url)">Previous</a></li> <li class="page-item disabled"><a class="page-link text-dark" href="#"> Page {{ pagination.current_page }} of {{ pagination.last_page }}</a></li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"> <a class="page-link" href="#" @click="fetchArticles(pagination.next_page_url)">Next</a></li> </ul> </nav> <div class="card card-body mb-2" v-for="article in articles" v-bind:key="article.id"> <h3>{{ article.title }}</h3> <p>{{ article.body }}</p> </div> </div> </template> <script> export default { data() { return { articles: [], article: { id: '', title: '', body: '' }, article_id: '', pagination: {}, edit: false } }, created() { this.fetchArticles(); }, methods: { fetchArticles(page_url) { let vm = this; page_url = page_url || '/api/articles' fetch(page_url) .then(res => res.json()) .then(res=> { this.articles = res.data; vm.makePagination(res.meta, res.links); }) .catch(err => console.log(err)); }, makePagination(meta, links) { let pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev } this.pagination = pagination; } } } </script>
    Here are a couple of supplemental videos that further explain Vue.js principles: Vue.us In 60 Seconds Coder's Tape -> an entire library of quick and easy videos that elaborate on Vue.js and Laravel
    assigning the data that we're getting back from our method to the articles[] array we're returning this is Vue.js code. We're getting ready to loop through the associative array that we've got with . The one thing that's a little unique is the key that Vue.js expects which is what I've got highlighted here: v-for="article in articles" v-bind:key="article.id" You see this explained in the Vue.js documentation: <div v-for="item in items" v-bind:key="item.id"> <!-- content --> </div> card card-body mb-2 -> these are Bootstrap classes for "cards." mb-2 is another Bootstrap setting that stands for "margin body." This is what gives us a little extra space. you're adjusting your fetchArticles a little bit by passing in a "page_url" variable. If that variable is present, you're going to be fetching those articles with the fetch functionality that coincide with that particular value. Otherwise, you're just grab what's coming from the "normal" api/articles dynamic. vm stands for "view model." When you instantiate a "View" instance, you automatically have access to all the functionality contained within the "View" dynamic. "Pagination" is one of those things that is provided through that package. this is the pagination code that you can get from Bootstrap v-bind is what Vue.js uses to handle dynamically changed entities based on incoming data. 4) Delete Article (back to top...) We're going to start by adding a "delete" button on the card: <button @click="deleteArticle(article.id)" class="btn btn-danger">Delete Articles</button> Next, we'll add this function: deleteArticle(id) { if(confirm('Are you sure?')) { fetch(`api/article/${id}`, { notice the " ` " character. You may very well get another effort method: 'delete' }) .then(res => res.json()) .then(data => { alert('Article Removed') this.fetchArticles(); }) .catch(err => console.log(err)); } } 5) Add Article Form (back to top...) Here's your first piece: <input type="text" class="form-control" placeholder="title" v-model="article.title"> Take a look at what "v-model" is all about:
    v-model -> You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases (from the Vue.js docs).
    Here's your input for your body and your button: <div class="form-group"> <textarea class="form-control" placeholder="body" v-model="article.bodyu"></textarea> </div> <button type="submit" class="btn btn-light btn-block">Save</button> Now, here's your code. You're going to be using the same method for both the "add" and "update" functionality with some obvious differences depending on the "edit" value which is set to "false" by default. Here's your "addArticle" code:
    addArticle() { if(this.edit === false) { //add fetch(`api/article`, { method: 'post', body: JSON.stringify(this.article), headers: { 'content-type': 'application/json' } }) .then(res => res.json()) .then(data => { this.article.title = ''; this.article.body = ''; alert('Article added'); this.fetchArticles(); }) .catch(err => console.log(err)); } else { //update } }
    Now, we add the "edit" dynamic: First of all, here's your button: <button @click="editArticle(article)" class="btn btn-warning mb-2">Edit Article</button> ...and here's your code:
    addArticle() { if(this.edit === false) { //add fetch(`api/article`, { method: 'post', body: JSON.stringify(this.article), headers: { 'content-type': 'application/json' } }) .then(res => res.json()) .then(data => { this.article.title = ''; this.article.body = ''; alert('Article updated'); this.fetchArticles(); }) .catch(err => console.log(err)); } else { //update fetch(`api/article`, { method: 'put', body: JSON.stringify(this.article), headers: { 'content-type': 'application/json' } }) .then(res => res.json()) .then(data => { this.article.title=''; this.article.body=''; alert('Article Updated'); this.fetchArticles(); }) .catch(err => console.log(err)); } }, editArticle(article) { this.edit = true; this.article.id = article.id; this.article.article_id = article.id; this.article.title = article.title; this.article.body = article.body; }
    first of all, it's worthwhile to revisit the fact that the "edit" value has been set to "false" by default. That was the edit: false piece in your "export" part of the code. fetch() is an "out of the box" piece of syntax that's designed to work with an API and provide a response that you can then configure however you want. Here's some material from the docs... const response = await fetch('http://example.com/movies.json'); const myJson = await response.json(); console.log(JSON.stringify(myJson)); You'll notice that the code above differs from what we wrote. This is "await" vs "promise. "await" is the way to go and you can read more about it here. The next two lines: .then(res => res.json()) .then(data => { The first "promise" returns your response which, in this case, you're formatting to JSON. The second promise gives you the chance to take that data and do something with it. Click here to watch a tutorial about it. PUT requests are a JavaScript thing. Here's a good explanation: PUT requests will be used to make updates to existing resources. It’s important to note that PUT is used to replace the entire resource – it doesn’t do partial updates (I will show you how to do this with PATCH in the future). Return to the controllers/employees.js file and add the following code to the bottom...
    ...this is from JavaScript and Oracle
    async function put(req, res, next) { try { let employee = getEmployeeFromRec(req); employee.employee_id = parseInt(req.params.id, 10); employee = await employees.update(employee); if (employee !== null) { res.status(200).json(employee); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.put = put;
    although I've documented this as two distinct bullet points, this is all one big, function... fetch(`api/article`, {method: 'put', body: JSON.stringify(this.article), headers: { 'content-type': 'application/json' } "body," in this case is going to be processed as the comprehensive "body" of info you're going to insert into the database. we talked about this earlier in the context of "fetch()." These are the standard lines of code found in your typical "fetch..." .then(res => res.json()) .then(data => { this.article.title=''; this.article.body=''; alert('Article Updated'); this.fetchArticles(); }) And that's it! A) Incorporating Security (back to top...) The challenge here is to incorporate all of the "Register" and "Login" links etc and superimpose all of that over the Bootstrap template that you currently have in place. 1) Setup Database (back to top...) First of all, you're running Laravel 6, so there's some nuances that you need to be aware of. You see that when you're setting up your tables.
    • begin by setting up your database and adjusting your ".env" file
    • alter your AppServiceProvider.php file in your "app/Providers" directory with this:
    public function boot() { // Schema::defaultStringLength(191); // this prevents an error to get thrown that says a particlar key is too long (click here for more info) } 2) Migrations and Page Creation (php artisan make:auth) (back to top...) Again, there are things about version 6 that differ from some of the tutorials that assume you're using version 5. To do all your table migrations and set up your pages, you have to do the following:
    composer require laravel/ui php artisan ui vue --auth php artisan migrate
    This works after you've set up your database. Also, to determine the version of Laravel you're using, you'll run this command:
    Bruce@WINDOWS-2SH5T3I MINGW64 /c/wamp64/www/new_nomas $ php artisan --version Laravel Framework 6.1.0
    At that point, you've got everything you need to login, register, retrieve a forgotten password...it's awesome! 3) Understanding and Setting Up the Login Screen (back to top...) When you run the above commands, you're doing more than what might appear to be the case on the surface. One example of that is the way in which the login process and routing is now firing automatically in ways that aren't necessarily obvious. For example, those pages that can only be accessed by an authenticated user are protected by the incorporation of middleware that you reference in your Controller. Take your AdminController.php page. You'll most likely have something like this: public function __construct() { $this->middleware('checkRole:admin '); } That Middleware has to be registered. To do that, you'll add 'checkRole' => \App\Http\Middleware\CheckRole::class, to your "Kernel.php" page. That is now firing with every request that's made to that page. Notice, it's checking the role of the person who's supposedly logged in and already authenticated. To trace the routing and the nuts and bolts of what's actually happening, you have to go to the Middleware itself. With "checkRole," you'll go to the "CheckRole.php" page to see what's happening. But here's something to be aware of: When all of this is orginally set up, you automatically have routes set up for "login" and all of the other authentication pages you'll need. On the "web.php," you'll see this: Auth::routes();. That's referring to a collection of routes on the "Router.php" located in the "vendor\laravel\framework\src\Illuminate\Routing" directory. Here are those routes:
    public function auth(array $options = []) { // Authentication Routes... $this->get('login', 'Auth\LoginController@showLoginForm')->name('login'); $this->post('login', 'Auth\LoginController@login'); $this->post('logout', 'Auth\LoginController@logout')->name('logout'); // Registration Routes... if ($options['register'] ?? true) { $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register'); $this->post('register', 'Auth\RegisterController@register'); } // Password Reset Routes... if ($options['reset'] ?? true) { $this->resetPassword(); } // Email Verification Routes... if ($options['verify'] ?? false) { $this->emailVerification(); } }
    When you try to pop the hood on "Auth/LoginController@showLoginForm," you might find yourself feeling a little exasperated because you won't see a function called, "showLoginForm." But you will find it on the "AuthenticateUsers.php" page which is referenced at the top: use Illuminate\Foundation\Auth\AuthenticatesUsers; On that page, you'll see this: public function showLoginForm() { return view('auth.login'); } B) Positioning of Login | Register Links (back to top...) You needed to make a couple of changes in your template. Here's what happened... 1) welcome.blade.php
    <div class="top-area"> <div class="container"> <div class="row"> <div class="col-md-12"> <div class="row"> <div class="col-xs-6 training_schedule"> <b>NOMAS<sup>®</sup> Training Schedule</b> </div> <div class="col-xs-6 training_register"> <!-- start login links--> @if(Auth::check()) <a href="{{ route('dashboard') }}">Dashboard</a> <form method="Post" id="logout-form" action="{{ route('logout') }}">@csrf</form> <a href="#" style="font-weight:bold;" onclick="document.getElementById('logout-form').submit();">Logout</a> @else <a href="#" style="color:#fff; font-weight:bold;" href="{{ route('login') }}">Login</a> |  <a href="#" style="color:#fff; font-weight:bold;" href="{{ route('register') }}">Register</a> @endif <!-- end login links --> </div> </div> </div> </div> </div> </div>
    The highlighted sections are where we added a row within a row to accommodate the "Login | Register" links to line up on the top right hand section of the site. 2) style.css (back to top...) To keep the new top section looking good, some changes had to be made in the "style.css" file. It's a hefty piece of code. The parts that are relevant are the classes of "training_register" and "training_schedule." Here's an example of what you see for the "@media (max-width:367px) {" section on line #2121. .training_register { text-align:right; font-size:6pt; } .training_schedule { font-size:6pt; } While the "auth" piece puts your basic login infrastructure in place, you will need additional functionality for your Admin Suite. First thing you're going to do is create your "roles" table...
    Bruce@WINDOWS-2SH5T3I MINGW64 /c/wamp64/www/new_nomas $ php artisan make:model Role -m Model created successfully. Created Migration: 2019_12_28_013531_create_roles_table
    C) User Login and Administrator (back to top...) A lot of the infrastructure you need to login is already in place by default thanks to the "auth" command. Still, there were some additional concepts and elements that had to be added in order to get this puppy to fire correctly. Regular User vs Administrator What's going to determine whether or not a user is a basic customer or an administrator is the value of "1" in the admin column in the user's table. That column had to be added. The "Login | Register" links at the top were changed so they now displayed, "Admin Page | Dashboard | Logout" if a user was logged in AND had administrative privileges. Here's the code that made all that happen:
    <!-- start login links--> @if(Auth::check()) @if(Auth::user()->admin==1) <a href="{{ route('adminDashboard') }}" style="color:#fff; font-weight:bold;">Admin Page</a> | <a href="{{ route('userDashboard') }}" style="color:#fff; font-weight:bold;">Dashboard</a> | <form method="Post" id="logout-form" action="{{ route('logout') }}" style="display:inline;">@csrf</form> <a href="#" style="font-weight:bold; color:#fff;" onclick="document.getElementById('logout-form').submit();">Logout</a> @else <a href="{{ route('userDashboard') }}" style="color:#fff; font-weight:bold;">Dashboard</a> | <form method="Post" id="logout-form" action="{{ route('logout') }}" style="display:inline;">@csrf</form> <a href="#" style="font-weight:bold; color:#fff;" onclick="document.getElementById('logout-form').submit();">Logout</a> @endif @endif @if(Auth::guest()) <a href="/login" style="color:#fff; font-weight:bold;" >Login</a> |  <a href="#" style="color:#fff; font-weight:bold;" href="{{ route('register') }}">Register</a> @endif <!-- end login links -->
    "Auth" is an object instantiated when a user logs in. You have access to several properties / values within that object. "check" gives you the ability to see if a user has logged in. the closest I came to a nested IF statement. If a user is logged in, the next step is to see if they've got admin permissions if the user is merely a guest as opposed to someone who has logged in, this is the verbiage they'll see displayed. By the way, a great video to watch that details permissions in the context of various users and tasks is available here. D) Pages (back to top...) 1) Pages Table (back to top...) To create an interface that allows you to create and update pages, the first thing you'll need to do is create the "pages" table. There's more than one way to skin a cat, but here's a good method that kills a couple of birds with one stone. Here's a command that creates the table as well as its corresponding model... php artisan make:model Page -m Running that code gives you the basic table as well as the Model which is stored in the "app" directory. After the basic table is created, you're going to want to put in some new columns. To do that, go to the "database/migrations" directory and edit the code that represents the schema of the newly formed "pages" table.
    Warning: Undefined variable $coede_frame in /home/cgob4051moa0/public_html/adm/laravel/index.php on line 4560
    public function up() { Schema::create('pages', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->binary('body'); $table->timestamps(); }); }
    After you do that, run php artisan migrate and that will run the new code you just wrote resulting in the "pages" table with all of the fields you've got documented above. BTW: "blob" is "binary..." You an see a complete list of data types by clicking here. 2) Insert Pages (back to top...)