Lasted edited: December 4, 2022

A work in progress. Maybe.

Creating A Recipe App To Learn Laravel/Mysql/Inertia/Vite/React

Intro

In August 2022, I started to put together a shopping list app to learn Laravel/Mysql/Inertia/Vite/React.

The app can be seen here. You can try it out, too.

As of December 3, 2022, the shopping list app is working well. It’s very simple. Just a list with a delete button next to to each item.

Shopping List App

The menu tab works.

Shopping List App With Menu Open

And there are different menus if the user is logged out.

Shopping List App With Menu Open

The password reminder email also works.

I’ve used React for the front end and git to track the changes.

It’s where I want it to be.

And you can see my blog post about my experience making the shopping list here.

Now, I want to push my learning further by creating a recipe app.

Here I go.

Recipe App Data Design

Tables needed for a recipe app.

Users - standard Laravel user table.

Recipes - table will have the name of the recipes along with the name of the user who created the recipe.

Ingredients - name of the ingredients that belong to each recipe. Ingredients should also have the name of the user who created the recipe. There will also be some ingredients that will be added by the admin user (me).

Laravel/Mysql Table Relationships

We all know how difficult personal relationships can be.

Mysql relationships aren’t much easier.

By creating relationships between tables, Laravel makes it easier to extract the data with predefined methods. You see, that’s one of the things about using Laravel. You need to understand the different methods (i.e. - functions) that are built into the framework.

And so, let’s think about the different relationships.

The first relationship is One To Many.

This should apply between the Users Table and the Recipes Tables.

Each User could create multiple recipes. One to Many. And, each user could also create multiple ingredients.

Relational databases has specific terms to describe the relationship between different tables.

Here’s an article on database tutorials from dotnettutorials.

One to Many is the most common database relationship. For example, in my shopping list app, one user has a relationship to many items. Of course, there is also an inverse relationship where certain items belong to only one user.

In Laravel eloquent, a one to many is called HasMany. And the inverse relationship where the many items belonging to one user is called belongsTo.

Once these relationships are defined, Eloquent makes it easier to retrieve the appropriate records.

This is how I first created the store function in my shopping app:

public function store(Request $request)
	{
        $validated = $request->validate([
            'name' => ['required', 'max:20'],
        ]);
        //creates an object item from the Class Item
        $item = new Item;
        //name is the property of $item and its value is assigned from the request
        $item->name = $request->name;
        // Stores id of user  into user_id
        $item->user_id = Auth::id();

        $item->save();

        return Redirect::route('items.index');
    }

Instead, in the bootcamp tutorial, it is recommended to use relationships and the function is written like this:

public function store(Request $request)
    {
        //
        $validated = $request->validate([
            'name' => 'required|string|max:25',
        ]);
 
        $request->user()->items()->create($validated);
 
        return redirect(route('items.index'));
    }

The first and last statements are basically the same as in the store function I created.

It’s the middle part that is totally different.

In the bootcamp version, the middle statement is creating a record that will belong to the logged in user by leveraging an items relationship.

In the below code, items() is a hasMany method that is created in the User Model at app/Models/User.php.

<?php
 ...
class User extends Authenticatable
{

public function items()
    {
        return $this->hasMany(Item::class);
    }

This items() method defines a hasMany relationship.

The first argument passed to the hasMany method is the name of the related model class (i.e. - Item::class).

Once the relationship is defined, Eloquent will automatically determine the proper foreign key column for the Item model.

By convention, Eloquent will take the “snake case” name of the parent model (User here) and suffix it with _id. So, in this example, Eloquent will assume the foreign key column on the Item model is user_id.

This will allow Eloquent to find all of the user’s items.

Then to find the user from the item, the inverse relationship must also be created. It is called belongsTo.

 <?php
 ...
class Item extends Model
{
 ...
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Once these relations are created, the store function works likes this:

 Public function store(Request $request)
    {
        //
        $validated = $request->validate([
            'name' => 'required|string|max:25',
        ]);
 
        $request->user()->items()->create($validated);
 
        return redirect(route('items.index'));
    }

And the index function in Item Controller can be written like this:

 public function index()
	{
        $user = Auth::user();
        $items = Item::whereBelongsTo($user)->latest()->get();
        return Inertia::render('Index', ['items' => $items]);
	}

Before using relationships, index was written like this:

public function index()
	{
    	$items = Item::all()->sortByDesc('id')->values();
        return Inertia::render('Index', ['items' => $items]);
	}

Recipe App Mysql Table Relationships

MYSQL Tables

Users Table

User-ID User-Name
1 Sam
2 Sadie

Recipes Table

Recipe-ID Recipe-Name Foreign-Key User-ID
1 Pizza 2
2 Bread 1
3 Tortilla 2
4 Rolls 1

Ingredients Table

Ingredient-ID Ingredient-Name Foreign-Key Recipe-ID
1 flour 1,2, 3
2 water 1,2, 3
3 sugar 1,2

Between Users and Recipes, there is one User with multiple recipes. This is a one to many relationship.

In Laravel eloquent, we call this hasMany.

It needs to be defined in the User Model (every table has to have a Model).

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model

{
   /**
    * The recipes that belong to the User.
    */
    
   public function recipes()
   {
       return $this->hasMany(Recipe::class);
   }
}

When you have a one to many, you also have the inverse relationship of many to one. In Laravel, we have belongsTo.

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Recipe extends Model

{
   /**
    * The users that belong to a Recipe.
    */
    
   public function users()
   {
       return $this->belongsTo(User::class);
   }
}

Many To Many Relationship

Unlike “Users to Recipes” (One To Many) , with “Recipes to Ingredients,” we have a “Many To Many” Relationship.

Each recipe will have many ingredients. And each ingredient will have many recipes.

Recipes Table

Recipe-ID Recipe-Name Foreign-Key User-ID Foreign-Key Ingredient-ID
1 Pizza 2 1, 2, 3
2 Bread 1 1, 2, 3
3 Tortilla 2 1,2
4 Rolls 1 1, 2, 3

Ingredients Table

Ingredient-ID Ingredient-Name Foreign-Key Recipe-ID
1 flour 1,2, 3, 4
2 water 1, 2, 3, 4
3 sugar 1, 2, 4

But you can’t define a multiple values in a row of a column. So, you can’t have multiple values in “Foreign-Key Ingredient-Id” or “Foreign-Key Recipe-Id.”

Instead, to define this many-to-many relationship, three (3) database tables are needed: “ingredients,” “recipes,” and “ingredient_recipe.”

The “ingredient_recipe table” is derived from the alphabetical order of the related model names and contains “ingredient_id” and “recipe_id columns.” This table is used as an intermediate table linking the ingredients and recipes.

The relationship’s table structure is:

ingredients  
id integer
name string
recipes  
id integer
name string
ingredient_recipe  
ingredient_id integer
recipe_id integer

As pointed out in Laravel docs, Many-to-many relationships are defined by writing a method that returns the result of the belongsToMany method.

The belongsToMany method is provided by the Illuminate\Database\Eloquent\Model base class that is used by all of your application’s Eloquent models.

For example, below is a “ingredients” belongstomany method on the “Recipe model”. The first argument passed to this method is the name of the related model class, “Ingredient.”

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Recipe extends Model
{
    /**
     * The ingredients that belong to the recipe.
     */
    public function ingredients()
    {
        return $this->belongsToMany(Ingredient::class);
    }
}

Once the relationship is defined, you can access the Recipe’s ingredients using the “ingredients” relationship property.

use App\Models\Recipe;
 
$recipe = Recipe::find(1);
 
foreach ($recipe->ingredients as $ingredient) {
    //
}

And you can access the Ingredients’ recipes by first creating an inverse belongstoMany “recipes” function in the “Ingredient” Model.

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Ingredient extends Model
{
    /**
     * The recipes that belong to the ingredient.
     */
    public function recipes()
    {
        return $this->belongsToMany(Recipe::class);
    }
}

The the recipes that contain known ingredient can be reached with:

use App\Models\Ingredient;
 
$ingredient = Ingredient::find(1);
 
foreach ($ingredient->recipes as $recipe) {
    //
}

Today, there was a lot of publicity about Chatgpt.

I asked the ai bot to explain and provide example code for “many to many” in laravel.

This is how it responded:

In Laravel, a "many to many" relationship refers to a relationship between two models where one model can have many instances of the other model, and vice versa. For example, a blog post may have many tags, and a tag may be used in many blog posts. In a many to many relationship, a join table is used to connect the two models, with a foreign key for each model in the join table. This allows you to easily retrieve all of the related records for a given model instance, and also to attach or detach related records as needed.

Here is an example of how you might create a many to many relationship and use a join table in Laravel:

First, you would define the relationship between the two models in their respective classes. For example, if you have a Post model and a Tag model, you might define the relationship like this in the Post model class:

public function tags()
{
    return $this->belongsToMany('App\Tag');
}

And in the Tag model class, you would define the relationship like this:

public function posts()
{
    return $this->belongsToMany('App\Post');
}
Next, you would create a migration to create the join table in the database. The join table will typically have a name that is the combination of the two model names, in alphabetical order, and the suffix _pivot. So, in this case, the join table would be named post_tag_pivot. You would create the join table like this:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostTagPivotTable extends Migration
{
    public function up()
    {
        Schema::create('post_tag_pivot', function (Blueprint $table) {
            $table->unsignedBigInteger('post_id');
            $table->unsignedBigInteger('tag_id');

            $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
            $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
        });
    }

    public function down()
    {
        Schema::dropIfExists('post_tag_pivot');
    }
}

Once you have defined the relationship and created the join table, you can use Eloquent methods to attach and detach related records, as well as retrieve related records. For example, to attach a tag to a post, you could do this:

$post = App\Post::find(1);
$tag = App\Tag::find(2);

$post->tags()->attach($tag);

To retrieve all of the tags for a given post, you could do this:

$post = App\Post::find(1);
$tags = $post->tags;

And to detach a tag from a post, you could do this:

$post = App\Post::find(1);
$tag = App\Tag::find(2);

$post->tags()->detach($tag);

I hope this helps! Let me know if you have any other questions.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Recipe extends Model { /** * The ingredients that belong to the Recipe. */ public function ingredients() { return $this->belongsToMany(Ingredient::class); } }

use App\Models\Recipe;

$recipe = Recipe::find(1);

foreach ($recipe->ingredients as $ingredient) { // }

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Ingredient extends Model { /** * The recipes that belong to the ingredients. */ public function recipes() { return $this->belongsToMany(Recipe::class); } }

Retrieving Intermediate Columns Of many-to-many

use App\Models\Recipe;

$recipe = Recipe::find(1);

foreach ($recipe->ingredients as $ingredient) { echo $incredient->pivot->created_at; }

My Todo List

  1. Would like to learn how to use a lookup table so that a user can choose a store where the product can be found. In other words have a separate table named shops that’ll can be selected and added to the item. Totally useless. Maybe a pantry table where you select items that can be added to shopping list.
  2. Make a recipe app.

Background.

I run this genealogy website.

Unlike a typical genealogy website, my focus has switched from finding the “second cousin of my great-grandfather” to integrating world historical information to my family history. I currently use Webtrees which is a great open source genealogy application.

One tool that I think will work well for a history/genealogy website is a timeline. Webtrees has a way of adding historical facts to a timeline. And I have written a [holocaust-timeline module}(https://github.com/murwell/Holocaust-Timeline) to add facts to a webtrees timeline. But I want something more than what is offered by Webtreees.

My initial thoughts was to use VUE with Laravel. But I found an impressive timeline component that uses react. [It’s called react-chrono and can be found on github]https://github.com/prabhuignoto/react-chrono).

Summary Of Process To Create A Laravel App With React

This summary uses “items” as the name of the example table. It will likely be different in your app.

  1. Create MYSQL Database.
    Mysql
    Create database name_db
  2. Create Laravel Project
    composer create-project laravel/laravel --prefer-dist laravel_name_app
  3. Add database info to .env file in root of app
    vim .env
  4. Install the authentication package into the laravel app.
    composer require laravel/breeze --dev
  5. Install React php artisan breeze:install react
  6. Need to Run NPM
    npm install && npm run dev
  7. Start Server
    php artisan serve
  8. Make Table Name - Singular - Capital Letter
    php artisan make:model Item --migration
  9. Add Schema To Migration File
  10. Add fillable properties to Model. Must be done if create method is to be used.
  11. Make Table with columns.
    php artisan migrate
  12. Add Controller.
    php artisan make:controller ItemController --resource.
  13. Add under namespace to Controller just made.
    use Inertia\Inertia;
  14. Add use App\Http\Controllers\ItemController; to routes/web.php.

  15. Store images in storage/app/public. Create symbolic link by running php artisan storage:link. Then images can be reached by: storage/image.jpg - EXAMPLE - <img src="storage/example-image.jpg">

Building a Shopping List With Laravel, Inertia, Vite, React, and MYSQL

Here’s my app to make a shopping list. It’s a work-in-progress.

First:

composer create-project laravel/laravel --prefer-dist react1-app

mysql  

Tip: To not have to use a password with mysql, create a filed called .my.cnf in the user ~ directory. Add the following to this file and substitute with the real name/password of the admin database user. You also need to have [client].

[client]  
user=admin_user_name  
password=admin_user_password. 

Now, to create a database for the app.

create database shopping1_db;

exit;

cd react1_app   (change into the larval app directory)

Edit the .env file and add in the database name, user, password.

vim .env 

To install the authentication package into the laravel app:

composer require laravel/breeze --dev

To install react:

php artisan breeze:install react

Then:

npm install && npm run dev

To start the server, make sure you’re in the app root directory:

php artisan serve

At this point, a generic app will be running.

A helpful app to install is the laravel debug bar.

composer require barryvdh/laravel-debugbar --dev

If the debug bar is not working, then check that in the .env file, app_debug=true.

At this point, there’s a database that’s connected to Laravel. But the database is empty. To make the tables that need to be included for the authentication app:

php artisan migrate

Once this runs, the database with have tables to create a user, password, etc.

For the shopping list app, I’ll also need a table for the items.

Larval has a neat way to create tables, it’s called migrations. For my app, the items table migration file needs to be created first:

php artisan make:migration create_items_table

Or

php artisan make:model Item --migration	

The above command will create a Model and migration file at the same time.

php artisan make:model Item -mcr

This command will hit the jackpot and make the Model, migration and controller at the same time.

There’s more explanation about Models and Controllers below.

Switch to the migrations folder that is stored in the database folder.

cd database/migrations

In that folder, there will be a new filed called something like 2022_08_13_005708_create_items_table.php.

In this file, a schema is automatically added that will eventually cause columns to be inserted into the table.

Schema::create('items', function (Blueprint $table) {  
             $table->id();  
             $table->timestamps();  
         });  

And so, there are two columns that are automatically included:

id() - which is the primary key  
timestamps() - which will cause two columns to be made in the table - "created_at" and "updated_at"

We’re going to have to add at least three more columns in the items table for the shopping list app.

$table->string('name');  (which is going to be the name of the shopping item - "bread" for example)

And these two statements are important:

$table->bigInteger('user_id')->nullable()->unsigned();  
$table->foreign('user_id')->references('id')->on('users');   

The purpose of these statements is to create a column called user_id that’ll store the id of the user from the users table.

This way when you’re later going to read the data, you can limit the shopping list items shown to the user who’s signed in. In other words, my friend doesn’t need to the see all the crap I buy.

To create the tables with these columns in the database, run:

	php artisan migrate

And now, the table items has been created with following columns;

columns in the table items

So, id is the primary key for each name/item that’ll be in the shopping list.

created_at and updated_at are self-explanatory.

name is the specific item that’ll be in the shopping list.

user_id is the specific user who is logged in.

By the way, the command to add a column to an existing table is:

php artisan make:migration add_ColumnName_to_TableName --table="name-of-existing-table"

Example where I want to add a “description” column to the table “items”:

php artisan make:migration add_description_to_items --table="items" 

MODEL

Laravel has an MVC architecture.

MVC stands for Model-View-Controller.

Model is basically the backend end. View is the frontend. Controller is the way to interact between the Model and the Frontend.

Model creates a class with the name of the Model. That class is then used in controllers to create functions.

Every table in the database has to have a model. To make a model, use the following command. Here the model name is Item. Laravel uses this naming convention where when you make the Item Model, it will assume that it connects to the ‘items’ table.

php artisan make:model Item

The command to create a migration file can be combined with this command as follows:

php artisan make:model Item --migration	

This would create the migration file date_create_items_table and the model file Item.php.

php artisan make:model Item -mcr

The above command will create a Model, migration, and controller file at the same time.

Make sure to add ‘use App\Models\Item;’ to the ItemController.php.

And then when you run php artisan migration, it will create the table named “items”, the model “Item.php”, and “ItemController”.

ROUTING

To see all registered routes use the following command:

 php artisan route:list 

Now that I have the database structure completed, let me talk about routing which is the way the app will know what to return when the user goes to a certain web address.

There’s a folder called routes. In that folder, there’s a file called web.php.

The routes/web.php file defines routes that are for your web interface. These routes are assigned the web middleware group, which provides features like session state and CSRF protection. Laravel Routing

Any controllers that are added will have to be added to web.php with a use statement.

use App\Http\Controllers\NameController;

At this point, web.php looks like this.

<?php

use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Controllers\ItemController;

/*
|--------------------------------------------------------------------------
| 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('/', function () {
	return Inertia::render('Welcome', [
    	'canLogin' => Route::has('login'),
    	'canRegister' => Route::has('register'),
    	'laravelVersion' => Application::VERSION,
    	'phpVersion' => PHP_VERSION,
	]);
});

Route::get('/dashboard', function () {
	return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

require __DIR__.'/auth.php';

On the top of this file, Illuminate is Laravel’s code, and Inertia connects Laravel to REACT.

Laravel routes use this general format:

Route::get(URI, function() {

})'

The ‘URI - Uniform Resource Identifier’ is the page name. Here '/' represents the home page. If it was '/greeting' then it would refer to http://website.com/greeting.

The function() gives the behavior that should be followed. An easy example from Laravel is:

Route::get('/greeting', function () {
	return 'Hello World';
}); 

So, if you went to http://website.com/greeting, you’d see Hello World.

Our routing function is slightly more complex:

First Route

function () {
		return Inertia::render('Welcome', [
    		'canLogin' => Route::has('login'),
    		'canRegister' => Route::has('register'),
    		'laravelVersion' => Application::VERSION,
    		'phpVersion' => PHP_VERSION,
		]);
	}

Since this function includes Inertia, the Welcome file that is rendered is contained in resources/js/Pages/Welcome.jsx. If Inertia was not included in the app, then the default templating blades, resources/views/welcome.blade.php, would be called instead.

Anyway, the above function instructs the app to return the javascript component (Welcome.jsx) and the props (properties) that are contained between the brackets [ ]. Those props are passed to the rendered page.

And so the function has to be read in combination with resources/js/Pages/Welcome.jsx.

In that file, there is the following script at the top:

<script setup>
import { Head, Link } from '@inertiajs/inertia-vue3';

defineProps({
	canLogin: Boolean,
	canRegister: Boolean,
	laravelVersion: String,
	phpVersion: String,
})
</script>

Focusing on the defineProps portion, we see that in Welcome.jsx, four properties are defined.

The first two variables are “Boolean” (True or False) and the second two are “String” (text string).

Those variables get their values from the routing function.

'canLogin' => Route::has('login'),
    'canRegister' => Route::has('register')

Route::has(‘login’) and Route::has(‘register’) are checks to see if these Route actually exist. If they do, “True” is returned. When Breeze Authentication was installed, these particular routes were added. So, the value for ‘canLogin’ and ‘canRegister’ are both true.

'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,

The above two variables just assigned the string value of the application and php version.

Now, when we look through the rest of resources/js/Pages/Welcome.jsx, these values will be passed IF they appear.

But, in Welcome.jsx, the props of canLogin and canRegister do not appear. Instead, this react version of the page uses a conditional ternary operator.

The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (?), then an expression to execute if the condition is truthy followed by a colon (:), and finally the expression to execute if the condition is falsy. This operator is frequently used as an alternative to an if…else statement.

<div className="fixed top-0 right-0 px-6 py-4 sm:block">
                    {props.auth.user ? (
                        <Link href={route('dashboard')} className="text-sm text-gray-700 dark:text-gray-500 underline">
                            Dashboard
                        </Link>
                    ) : (
                        <>
                            <Link href={route('login')} className="text-sm text-gray-700 dark:text-gray-500 underline">
                                Log in
                            </Link>

                            <Link
                                href={route('register')}
                                className="ml-4 text-sm text-gray-700 dark:text-gray-500 underline"
                            >
                                Register
                            </Link>
                        </>
                    )}
                </div>

An so when we look at the top of resources/js/Pages/Welcome.jsx, we see that there is a conditional statement in the form of ? :.

Basically, if props.auth.user is true (i.e.- user is logged in), then the first link ‘dashboard’ is shown. If props.auth.usr is false (user is not logged in), then the links after : will be shown (‘login’ and ‘register’).

In the Vue version of this app, resources/js/Pages/Welcome.vue, the top of the file is written as:

 <div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0">
        <div v-if="canLogin" class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
            <Link v-if="$page.props.auth.user" :href="route('dashboard')" class="text-sm text-gray-700 dark:text-gray-500 underline">Dashboard</Link>

            <template v-else>
                <Link :href="route('login')" class="text-sm text-gray-700 dark:text-gray-500 underline">Log in</Link>

                <Link v-if="canRegister" :href="route('register')" class="ml-4 text-sm text-gray-700 dark:text-gray-500 underline">Register</Link>
            </template>
        </div>

Here caLogin and canRegister do appear after v-if statements.

The directive v-if is used to conditionally render a block. The block will only be rendered if the directive’s expression returns a truthy value.

So, in the vue version, the same results as in the react version, but using different terminology.

Okay, let me get back to the routes in web.php.

Second Route in web.php

Route::get('/dashboard', function () {
	return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Here we see that if you go to http://website.com/dashboard another function with Inertia is returned.

Here resources/js/Pages/Dashboard.vue will be rendered but only if the user is logged in.

Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to your application’s login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. See Laravel

Third Route in web.php

require __DIR__.'/auth.php';

This includes all the routes needed for its authorization software and is located at routes/auth.php.

Okay, I kind of know a little about routing, did the columns in the database (ie - migrations), guess it’s time for Controllers.

Controllers

Controllers work together with routing. They are stored at app/Http/Controllers.

The generic controller at app/Http/Controllers/Controller.php doesn’t explain very much.

The laravel documentation states:

Controllers are not required to extend a base class. However, you will not have access to convenient features such as the middleware and authorize methods.

Guess, we need those features. Time to construct a controller for my items table.

The naming convention for a controller is Upper Camel Case with Controller at the end and singular throughout. Since my tables is items, my controller will be:

php artisan make:controller ItemController --resource

When --resource is added, the made controller will contain the typical create, read, update, and delete (“CRUD”) routes.

And after running the above command, I now have this file:

app/Http/Controllers/ItemController.php

(As noted above, this ItemController.php has to be added to web.php with use app/Http/Controllers/ItemController.php;)

ItemController.php has the following boilerplate stuff:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ItemController extends Controller
{
	/**
 	* Display a listing of the resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
	public function index()
	{
    	//
	}

	/**
 	* Show the form for creating a new resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
	public function create()
	{
    	//
	}

	/**
 	* Store a newly created resource in storage.
 	*
 	* @param  \Illuminate\Http\Request  $request
 	* @return \Illuminate\Http\Response
 	*/
	public function store(Request $request)
	{
    	//
	}

	/**
 	* Display the specified resource.
 	*
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
	public function show($id)
	{
    	//
	}

	/**
 	* Show the form for editing the specified resource.
 	*
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
	public function edit($id)
	{
    	//
	}

	/**
 	* Update the specified resource in storage.
 	*
 	* @param  \Illuminate\Http\Request  $request
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
	public function update(Request $request, $id)
	{
    	//
	}

	/**
 	* Remove the specified resource from storage.
 	*
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
	public function destroy($id)
	{
    	//
	}
}

It now looks like I have to add the appropriate function into each of these sections.

But first, since this app is using inertia, at the top of this controller, use Inertia\Inertia; must be added.

So, the top of ItemController.php is now:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;

class ItemController extends Controller

Another important thing is that when the bare-bones functions are created in the controller by php artisan make:controller ItemController --resource, web.php, has to include the routes to these functions. This is done in one line of code.

Route::resource('items', ItemController::class);

And if I want the route to be protected by authentication, I can just add the authentication middleware ->middleware('auth'); to the route in the web.php file.

Route::resource('items', ItemController::class)->middleware('auth');

Once this code (with or without the middleware) is included, the following routes are created as shown in Laravel documentation. In Laravel’s chart, they were working with a controller named PhotoController. Here it’s ItemController. So just replace “photo” with “item”:

Chart of Routes Table From Laravel Documentation

This chart has to be read in conjunction with ItemController.php.

The seven items in the chart match the seven functions in the ItemController.

The next step will be to code the seven functions to match the chart’s actions.

A few of the functions, should just return a view (i.e.-:form) and the others a method to manipulate a table in the database.

Index displays a listing - that should be a view of all the items on the shopping list.

Create should be a view of a form that is used to create an item on the shopping listing. Basically, the name of the item to be inserted. Per documentation, “before using the create method, you will need to specify either a fillable or guarded property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default.” Mass assignment can be enable for safe attributes by marking them as “fillable”.

Store should be a method of storing the form called in Create. Store should be the way to take the name from the create form and store it into the name column in the items table.

Show should be the specified resource. Unlike index, it is only one item.

Edit should show the form that is used to edit the name of an existing item.

Update should take the resource from edit and modify it.

Destroy should be a way to delete the specific resource.

The first function to fill in is a

Make sure to add ‘use App\Models\Item;’ to the ItemController.php.

The second function is to return a route to the form that’ll create a shopping list item. But only logged in users should be able to create an item. So, I should just modify the Second Route from above.

public function create()
{
 	$item = Item::create([
        'name' => $request->name,
    ]);
return Inertia::render('Create');
})->middleware(['auth', 'verified'])->name('create');
}

Controllers, Functions, Methods, Classes, Oh my!

This concept of adding methods/functions to controller is hard for me to get my head around.

Web.php has some routes. I understand that when a person goes on the web ‘/”, the code at /pages/Welcome.jsx is rendered. It’s that concept of passing props/variable to the web page that is confusing to me. I’m sure most of it is because I don’t have the underlying knowledge required. But it is what it is.

The shorthand of Route::resource('items', ItemController::class)->middleware('auth'); is impressive. This idea that with this one line of code/boilerplate, numerous routes are created that are protected via authentication.

But I’m confused by this chart in Laravel documentation.

Verb URI Action Route Name
GET /items index items.index
GET /items/create create items.create
POST /items store items.store
GET /items/{item} show items.show
GET /items/{item}/edit edit items.edit
PUT/PATCH /items/{item} update items.update
DELETE /items/{item} destroy items.destroy

I understand that GET is telling us to return views for index, create, show, and edit. I assume that create and edit will be forms to create and edit. But what’s the difference between index and show. And why does post, put, and delete have separate routes.

After looking at stuff for a couple of weeks, it looks like the purpose of these different routes are to allow different functions in the controller. Namely, every action is attached to a different function in the controller.

Difference between show and index.

According to a discussion here, index will show all the entries in a table and show will only show one item.

That’s helpful.

So, for the first function index, this might work:

  public function index()
    {

        $items = Item::all();
        return Inertia::render('Index', compact('name'));
    }

According to laravel, the model’s all() method will retrieve all of the records from the model’s associated database table. And the compact() function creates an array from variables and their values.

For my shopping list app, I envision:

Get - index page - which would be a list of the items. And next to each item would be a button to delete. There could also be a button to update. And a button to add an item that would take you to create.

Overall View Of Laravel/Vite/Inertia/React

In July 2022, Laravel started using vite rather than mixjs to bundle frontend assets.

So, I guess I need to learn how Vite works. Here’s Laravel’s documentation on Vite.

Summary of Laravel Vite Documentation

When building applications with Laravel, you will typically use Vite to bundle your application’s CSS and JavaScript files into production ready assets.

Within a fresh installation of Laravel, you will find a package.json file in the root of your application’s directory structure. The default package.json file already includes everything you need to get started using Vite and the Laravel plugin. You may install your application’s frontend dependencies via NPM:

npm install [frontend dependency]

This is what the package.json file in my app looks like now.

package.json file image

This gives me a better idea of what vite does. It seems like I shouldn’t need to touch the package.json file which is controlled by Laravel and npm.

There’s an additional vite configuration file that can be used according to vite.

Vite is configured via a vite.config.js file in the root of your project. You are free to customize this file based on your needs, and you may also install any other plugins your application requires, such as @vitejs/plugin-vue or @vitejs/plugin-react.

If you are building an SPA (Single page Application), including applications built using Inertia, Vite works best without CSS entry points.

In other words, don’t add css into vite.config.js. Instead, import the css into ‘resources/js/app.js’ like this:

import './bootstrap';
import '../css/app.css';

By the way, vite.config.js is found in the root directory (not in the folder called app).

Adding An Email SMTP Server To Laravel App

In the .env file, you adjust the email info. Before doing that, you should stop the server.

php artisan down

Then go to the .env and put in the your values.

MAIL_MAILER=smtp
MAIL_HOST=smtp.your_smtp_server.com
MAIL_PORT=465
MAIL_USERNAME=support@your_website.com
MAIL_PASSWORD=password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="support@your_website.com"
MAIL_FROM_NAME="${APP_NAME}"

After making the changes, run:

php artisan config:cache

Restart the server.

php artisan up

From atymic.dev, this is how to test the email:

php artisan tinker

Mail::raw('Hello World!', function($msg) {$msg->to('test_email_recipient@gmail.com')->subject('Test Email'); });

When I tried this, I first got an error that the sender could not be authenticated.

After I ran php artisan config:cache, it worked.

Different App Bars (Navbars) Depending On User

I wanted to have different menus depending on whether the user is logged in or not.

When the user is logged out, I want links to “Register” or “Login.”

Logged Out App Bar

Those links don’t apply, if the user is logged in.

Logged In App Bar

To do this, I used the javascript conditional operator that is also called the ternary operator.

The general format is condition ? true result : false result.

Below, the condition checks if the user is authorized. If true, one appbar is used. If false, the LoggedOutBar is used.

<div >
     {props.auth.user ?
          <DrawerAppBar />
            :
          <LoggedOutBar />
      }
</div>

Centering the Registration Form

Off Centered Registration Form

The Registration Form had a big Laravel logo which I didn’t like. After I deleted the logo, it left a large gap on the top of the screen that looked ugly.

To correct this, I edited a line of code in resources/js/Layouts/Guest.jsx.

Before the edit:

<div className="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">

I deleted min-h-screen and bg-gray-100:

<div className="flex flex-col sm:justify-center items-center pt-6 sm:pt-0">

It now looks much better.

Centered Registration Form

Need To Register?

The default login form that comes with Laravel did not have the “Need To Register?” line.

I believe the login form works better with it.

To get this link, I added to the “Login page” at resources/js/Pages/Auth/Login.jsx the following code:

    <div className="flex items-center justify-end mt-4">
                    <Link
                    href={route('register')}
                    className="underline text-sm text-gray-600 hover:text-gray-900"
                    >
                         Need To Register?
                    </Link>
    </div>

React Curley Braces

Using { } in jsx allows you to add javascript into the html markup.

Below is an example from React Beta Docs.

const today = new Date();

function formatDate(date) {
  return new Intl.DateTimeFormat(
    'en-US',
    { weekday: 'long' }
  ).format(date);
}

export default function TodoList() {
  return (
    <h1>To Do List for {formatDate(today)}</h1>
  );
}

Above today is assigned the current date and time through the javascript constructor.

The formatDate function takes the date and provides the day of the week.

The export default function has {formatDate(today)} within <h1> tags.

(today) calls new Date() and that date is formatted.

You get To Do List for Friday (This assumes the function is called (instantiated) on a Friday.

{{ }} -Double Curlies are used to pass a JS object in JSX. This is oftentimes seen with inline CSS styles in JSX.

<ul style={{
      backgroundColor: 'black',
      color: 'pink'
    }}>

Adding A Snackbar To The App

The way my shopping app works, an item is added to the top of the list. That makes it easy to see if the app works.

When an item is deleted, it’s sometimes hard to see if it’s actually be deleted. To overcome this issue, I decided to add a snackbar that would momentarily popup “Item Deleted” notification.

Basically, the “Item Deleted” pops up on the bottom of the screen when the delete button is pressed.

Snackbar Shopping App

To get this functionality, I added snackbar code to `Index.jsx’.

import React from 'react';
import ScopedCssBaseline from '@mui/material/ScopedCssBaseline';
import DrawerAppBarLoggedIn from './Components/DrawerAppBarLoggedIn';
import DrawerAppBar4a from './Components/DrawerAppBarLoggedIn';
import { Link, usePage, Head } from '@inertiajs/inertia-react';
import ItemCreate from './Components/ItemCreate';
import Paper from '@mui/material/Paper';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Button from '@mui/material/Button';
import Snackbar from '@mui/material/Snackbar';

export default function shoppingList(props) {

    const [open, setOpen] = React.useState(false);

    const handleClick = () => {
        setOpen(true);
    };

    const handleClose = (event, reason) => {
        if (reason === 'clickaway') {
            return;
        }
        setOpen(false);
    };

    return (
        <>
            <ScopedCssBaseline>
                <Head title="Sympile Shopping List" />
                <div >
                    {props.auth.user ? (
                        <DrawerAppBarLoggedIn />
                    ) : (
                        <>
                            <DrawerAppBar4></DrawerAppBar4>
                        </>
                    )}
                </div>
                <Container maxWidth="sm">
                    <Grid container justifyContent="center">
                        <ItemCreate></ItemCreate>
                    </Grid>
                    <br></br>
                    <Grid container justifyContent="center">
                        <TableContainer component={Paper}>
                            <Table sx={{ minWidth: 350 }} aria-label="simple table">
                                <TableHead>
                                    <TableRow>
                                        <TableCell>Shopping List</TableCell>
                                    </TableRow>
                                </TableHead>
                                <TableBody>
                                    {props.items.map((item, index) => (
                                        <TableRow
                                            key={item.id}
                                            sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
                                            data-index={index}>
                                            <TableCell>{item.name}</TableCell>
                                            <TableCell align="left">
                                                <Button variant="outlined" onClick={handleClick}><Link href={route('items.destroy', item.id)} method="delete" as="button" type="button" >Delete</Link></Button>

                                            </TableCell>
                                        </TableRow>
                                    ))}
                                </TableBody>
                            </Table>
                        </TableContainer>
                    </Grid>
                </Container>
            </ScopedCssBaseline>
            <Snackbar
                open={open}
                autoHideDuration={800}
                onClose={handleClose}
                message="Item Deleted"
            />
        </>
    );
}

Explanation of Snackbar Code

On the delete Button, the following onclick code was added.

<Button variant="outlined" onClick={handleClick}><Link href={route('items.destroy', item.id)} method="delete" as="button" type="button" >Delete</Link></Button>

Specifically, onClick={handleClick} calls a javascript function handleClick`.

handleClick is defined as follows:

const handleClick = () => {
        setOpen(true);
    };

handleClick makes setOpen(true).

const [open, setOpen] = React.useState(false);

useState is a React Hook that lets you add React state to function components.

In my app, useState appears like this.

const [open, setOpen] = React.useState(false);

A variable open is declared, and its value is set to false.

To change the value of open,setOpen is used.

Which brings us back to the function handleClick which calls setOpen(true) and updates open to true.

Button contains onClick={handleClick}.

So, when the button is clicked, the variable open is set to true by calling the function handleClick.

And snackbar contains:

<Snackbar
                open={open}
                autoHideDuration={800}
                onClose={handleClose}
                message="Item Deleted"
            />

The Snackbar “open” property calls the javascript “open” variable.

In other words, when the button is clicked, “open” gets set to “true” and the snackbar is opened.

This only took me about three days to figure out.

HTTPS Not Working Until I Changed An AWS Setting

Everytime I think I’ve turned the corner, I smash my head into a wall. This time it was moving my site from http to https.

I used certbot like I’ve done a hundred times before. It has always worked with no issues.

This time when I wanted to move from http to https I get unable to connect.

At first, I thought it was because I forgot to switch the field in .env from http to https.

APP_URL=https://shoppinglist.sympile.com

But, I switched it, cleared the cache, and it still didn’t work.

I then wasted another three to four hours until I found the problem.

The site is hosted on AWS. When the server is setup, there are some default firewall settings that stop ssl traffic (443) from connecting to the server.

All I needed to do was allow 443 traffic through and the problem was solved.

What a waste of time.

There are two virtual host files:

First, Unsecured.conf

<VirtualHost *:80>
   ServerName servername.com
   ServerAdmin webmaster@servername.com
   DocumentRoot /var/www/html/shoppinglist/public

<Directory /var/www/html/shoppinglist/public>
        Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
   </Directory>
   ErrorLog ${APACHE_LOG_DIR}/error.log
   CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =shoppinglist.sympile.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Second, Secured.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
   ServerName servername.com
   ServerAdmin webmaster@servername.com
   DocumentRoot /var/www/html/shoppinglist/public

   <Directory /var/www/html/shoppinglist/public>
       AllowOverride All
   </Directory>
   ErrorLog ${APACHE_LOG_DIR}/error.log
   CustomLog ${APACHE_LOG_DIR}/access.log combined


SSLCertificateFile /etc/letsencrypt/live/shoppinglist.servername.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/shoppinglist.servername.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Would have taken five minutes if only the firewall had permitted 443 traffic.

I’m such an idiot.

Deploying Laravel To A Remote Server On AWS

I have a somewhat working model of my shopping list app. I now want to deploy it to a server that is being hosted on AWS.

The server is using Apache2 and MYSQL.

There are some paid versions that would make it easier to deploy the app. But given this is a learning exercise, it’s time to figure out how to deploy for free.

At this point, I don’t know if it would be easier to deploy using docker or just github.

I was thinking of just figuring out a way of deploying using github. That way when I make changes locally, I see just pushing out the changes to the remote server. A software engineer suggested that I may want to use docker.

Should I Use Docker To Deploy A Laravel App

The only thing I know about Docker is that it’s some type of a container.

Laravel has Sail to interact with Docker.

Reading this Sail documentation is giving me a headache. I’m not sure if I have the patience to learn Sail to deploy a Laravel docker.

Here’s a tutorial by Gershan Manandhar that explains using Docker with Laravel. This tutorial is very impressive. It just that it recommends using Google Cloud Run to deploy the container. My server is on AWS. Not sure if I want to switch now to Google. I see that Amazon has Fargate. I think it’s a little too much for just my simple shopping app.

Docker is probably something I’ll learn in the future, I think I’m going to use Git to deploy my app.

Useful Git Commands

Commands to discard unstaged changes

git restore .

To discard unstaged changes for a specific file

git restore path/to/file/to/revert

Commands to discard all local changes that haven’t been committed:

  1. git reset –hard

  2. git clean -fxd

Switch branch

git switch name_of_branch

Delete branch

git branch -d name_of_branch

Force Delete Branch Where Branch Has Not been Merged (should roll back local changes first)

git branch -D name_of_branch

Using Git To Deploy Laravel App From A Private Git Repository

The first step is to clone a private repository is to connect to the remote server. For this example I’ll call the user - “user1” (very original).

ssh user1@ip_address

If you have not yet generated an ssh key for this user, you must do it now:

ssh-keygen - this generates a ssh. Do not use sudo.

Then cd .ssh - this will switch you into the hidden directory .ssh

cat id_rsa.pub - will show contents of public key

copy the contents of the public key.

Switch to the repository in github that you want to clone, then go to settings -> Deploy keys.

Click on the button Add deploy key

Give this key a title and paste in the contents of the public key for user1.

Go back to the remote server to the home directory of user1 - /home/user1.

mkdir repository (this will create a directory called repository) - do not use sudo. Because you are in you user’s home directory, you do not use sudo. Otherwise, the directory will belong to root and not to the actual user.

Remember, you did NOT use sudo to create the user’s ssh key. If you did, you will need to delete the public and private keys using sudo and then regenerate the keys without sudo.

cd repository - this is the directory where the github repository will be cloned.

The command to clone a repository is git clone git_address.

For my project it is:

git clone git@github.com:murwell/shopping.git

To clone without the top level folder shopping.

git clone git@github.com:murwell/shopping.git . - Notice the period after the space.

Do not use sudo.

This cloned the project to \home\user1\repository.

Done cloning.

Now, the cloned app is in \home\user1\repository\shopping.

cd \home\user1\repository - this switched me into the repository directory.

Since, sudo was not used to create the directory, the ownership of the directory is user1:user1.

Group permissions need to be worked out. This is something I still need to work out.

I gonna go back to the tutorial by Decode Web.

The tutorial is a few years old, so I don’t know what’s changed since then. Hopefully, nothing.

Here’s a different tutorial from Kenean that discusses how to automate a laravel app deployment using Github actions.

Got The Website Somewhat Running

I got the website somewhat running.

It was a disaster. Needed to delete files that had errors in them and needed to update node.js using nvm. Ubuntu comes with an old version of node.js. I didn’t know that. That old version caused lots of problems. Once I updated to node 18.12.1 with nvm, the app loaded fine.

To get it running, this tutorial from laravel daily helped.

These are my steps for getting laravel app running from github into a directory that is owned by user1 and getting stored in a directory called shoppinglist:

sudo mkdir /var/www/html/shoppinglist

sudo chown -R user1:user1 shoppinglist

cd /var/www/html/shoppinglist

git clone git@github.com:murwell/shopping.git . - run inside /var/www/html/shopplinglist and without sudo

vim .env - edit the .env file in the laravel app

composer install - adds the dependencies

php artisan key:generate - generates an application key and automatically adds it .env

php artisan migrate - migrates the schema to the database

npm ci

npm run build

cd /var/www/html

sudo chown -R www-data:www-data shoppinglist - changes ownership of app directory to www-data:www-data

Procedure To Update the App Where Git Is Used

Again, this tutorial from laravel daily helped.

cd /var/www/html/laravelapp

Changes into the laravel app directory

php artisan down

Shuts down laravel app for web users

cd .. 

Moves into /var/www/html

sudo chown -R user1:user1 laravelapp

Changes ownership of laravelapp directory to user1. Change user1 to the actual name of the user. Also, this is cumbersome. I will probably add user1 to the www-data to stopping these steps.

cd laravelapp

Changes directory into laravel app

git pull

Pulls the changes from to Git

composer install

Adds in any new dependencies

If you have created some new migrations, this is the time to run php artisan migrate

npm run build

Rebuilds the app. The first time I had made changes, I forgot to do this step. Without running npm run build, changes were not being seen by the web user.

cd ..

Changes directory up one level to /var/www/html

sudo chown -R www-data:www-data laravelapp

Changes ownership of laravelapp directory back to www-data

cd laravelapp

Changes directory back into laravelapp directory

php artisan up

Puts the laravel app back up

Laravel Commands That Are Needed

From KenFai at dev.to.

php artisan config:cache will cache the .env configuration file.

php artisan down for maintenance mode.

php artisan up to stop maintenance.

php artisan config:clear

php artisan config:cache

php artisan route:clear

php artisan route:cache

php artisan view:clear

php artisan event:clear

php artisan cache:clear - application cache cleared

php artisan optimize:clear - clear all caches

Problems Noticed With Production App

After getting the app running on a web server, here are some problems I’ve noticed:

  1. After logging in, the app redirects the page to the dashboard. I needed to change that and instead wanted the app to redirect the user to the actual app.

In the AuthenticatedSessionController.php, there is a line of code that redirects as follows:

        return redirect()->intended(RouteServiceProvider::HOME);

Home is defined in /app/Providers/RouteServiceProvider.php.

In that file there is public const HOME = '/dashboard';

I changed that to public const HOME = '/items';

Now, when someone logs in, the user is redirected to the app’s shopping list.

This is one of those issues with Laravel that has made it difficult for me (besides my lack of knowledge). Code is sometimes hidden behind other code. Without digging to find the buried treasure, just looking at the top layer doesn’t explain what’s going on.

  1. If you’re inactive on a page, the default setting is to timeout the page for security purposes. There is a plugin that should take care of this problem. I am still testing it out.

The plugin is called laravel-caffeine and is from GeneaLabs. It can be found on Github here.

  1. I made some changes to a few pages locally. I pushed the changes to Git. I then went to the server and updated the app. Once the pages were update on the remote server, the pages were not changing to the user on the web. I tried clearing caches, nothing seemed to work.

I then realized, I needed to run npm run build. That updated the app for the web user and all was good.

  1. The user gets signed out after some time. There are a couple of suggestions here that includes storing sessions in a table or extending the sessions lifetime value.

Initially, extening the sessions lifetime in app/config/session.php is the easiest solution. I will try that first and edit the sessions lifetime from 120 to 2880 minutes (i.e. - two days).

php artisan down

cd app/config

sudo vim session.php

edit ‘lifetime’ => env(‘SESSION_LIFETIME’, 120 2880)

php artisan config:clear

php artisan up

Learning React

I’m having difficulty with the front end. I’ve got a lot things working, but one of the problems are the menus. The logout button is complicated for me. Rather than just plowing through, I’m going to spend some time learning react.

There is a beta tutorial from React that I am reviewing. It can be found here. This section is taken from the beta tutorial. It is much clearer than react’s previous tutorial.

First, just learning the lingo is helpful. In React, reference is repeatedly made to adding “markup” to jsx files. “Markup” is html - HyperText Markup Language. And so, the right way to look at React is that it allows you to add html (markup) to javascript. Markup are html elements like <h1>, <img>, <ul>.

In React, rendering logic and markup are kept together in components. Each React component is a JavaScript function that may contain some markup that React renders into the browser.

The export default keywords specify the main component in the file.

To convert html to react, one can use a converter.

The general format of a component is:

export default function ComponentName() {
  return (
 
        );
}

A file can only have one default export, but it can have numerous named exports.

export function WhateverName() {
  // ...
}

To import a non-default component, use curly braces.

import { NonDefaultName } from './ComponentName.js';

For default components, no curly braces.

import DefaultName from './DefaultComponentFile.js';

React component names must always start with a Capital letter, while HTML tags must be lowercase.

Component names should use the PascalName format - ComponentName.jsx

To return multiple elements from a component, wrap them with a single parent tag or with <> … </>.

<div>
  <h1>Hedy Lamarr's Todos</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    class="photo"
  >
  <ul>
    ...
  </ul>
</div>

OR if you don’t want to add an extra <div> to your markup, you can write <> and </> instead:

<>
  <h1>Hedy Lamarr's Todos</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    class="photo"
  >
  <ul>
    ...
  </ul>
</>

Root Component File

The root component file is the file that is returned by the route. So, in my Laravel shopping app, when the URI /items route is called, the page *Index.jsx is the root component file.

Verb URI Action Route Name  
  GET /items index items.index
  GET /items/create create items.create
  POST /items store items.store
  GET /items/{item} show items.show
  GET /items/{item}/edit edit items.edit
  PUT/PATCH /items/{item} update items.update
  DELETE /items/{item} destroy items.destroy

Within the Root Component File, you would import other components, if needed.

If you use a framework with file-based routing, such as Laravel, your root component will be different for every page.

One thing that I noticed after looking at this for the millionth time is that I my have a flaw in my controller for create.

It is written like this:

/**
 	* Show the form for creating a new resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
	public function create()
	{
    	return Inertia::render('Create');
	}

This is a security issue as there is no check for authentication on the create page. Anyone would be able to add to the database.

My create page is:

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'


export default function Create() {
const { data, setData, post, processing, errors } = useForm({
  name: '',
})

function submit(e) {
  e.preventDefault()
  post('/items')
}

return (
  <form onSubmit={submit}>
    <input type="text" value={data.name} onChange={e => setData('name', e.target.value)} />

    <button type="submit" disabled={processing}>Submit</button>
    {errors.name && <div>{errors.name}</div>}
  </form>
)
}

This means that if someone goes to /items/create, they’ll be able to add to the database because the input box would be shown.

Input Box With Label

So, I changed the page to be rendered on create to the same code that is within the index function in ItemController.

/**
 	* Show the form for creating a new resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
	public function create()
	{
    	//get authenticated user who's making request via the Auth facade's user method
        $user = Auth::user();
        // gets the items that belongs to the authenticated user and sorts by latest
         $items = Item::whereBelongsTo($user)->latest()->get();
         //renders the index page and passes the prop 'items' that contains items attributes
         return Inertia::render('Index', ['items' => $items]);
	}

So far, it works. I pushed the change to Git.

JavaScript in JSX with Curly Braces

JSX lets you write HTML-like markup inside a JavaScript file, keeping rendering logic and content in the same place. Sometimes you will want to add a little JavaScript logic or reference a dynamic property inside that markup. In this situation, you can use curly braces in your JSX to open a window to JavaScript.

See here.

export default function TodoList() {
  const name = 'Gregorio Y. Zara';
  return (
    <h1>{name}'s To Do List</h1>
  );
}

Any JavaScript expression will work between curly braces.

Curly Brace Usage in Jsx

  1. As text directly inside a JSX tag: <h1>{name}’s To Do List</h1> works, but <{tag}>Gregorio Y. Zara’s To Do List</{tag}> will not.
  2. As attributes immediately following the = sign: src={avatar} will read the avatar variable, but src=”{avatar}” will pass the string “{avatar}”.
  3. To pass a JS object in JSX, you must wrap the object in another pair of curly braces. person={{ name: "Hedy Lamarr", inventions: 5 }}
  4. To pass an inline style use a pair of curly braces. Inline style properties are written in camelCase rather than normal css. (i.e. - backgroundColor NOT background-color) <ul style={{backgroundColor: 'black',color: 'pink'}}>

Conditional Rendering

<div>
  {isLoggedIn ? (
    <AdminPanel />
  ) : (
    <LoginForm />
  )}
</div>

I think this is key for me. In my app, I will try:

 <div>
      {props.auth.user ? (
          <NavBarLoggedIn />
       ) : (
          <LoginForm />
       )}
 </div>

No else branch

 <div>
	{props.auth.user && <NavBarLoggedIn />}
 </div>

Rendering Lists

const products = [
  { title: 'Cabbage', isFruit: false, id: 1 },
  { title: 'Garlic', isFruit: false, id: 2 },
  { title: 'Apple', isFruit: true, id: 3 },
];

export default function ShoppingList() {
  const listItems = products.map(product =>
    <li
      key={product.id}
      style= {{
        color: product.isFruit ? 'magenta' : 'darkgreen'
      }}
    >
      {product.title}
    </li>
  );
  

Responding to Events

 function MyButton() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

Using State To Remember Some Info

import { useState } from 'react';

function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

**If you render the same component multiple times, each will get its own state. **

So, in the above example, if you call multiple times, each MyButton will have its own state (value).

const [count, setCount) = useState(0);

useState creates a value and a function.

const [something, setSomething) = useState(value);

The function name should be a combination of “set” with the name of the value - “setSomething”.

Using Hooks

Functions starting with use are called Hooks. useState is a built-in Hook provided by React. You can find other built-in Hooks in the React API reference. You can also write your own Hooks by combining the existing ones.

Hooks are more restrictive than regular functions. You can only call Hooks at the top level of your components (or other Hooks). If you want to use useState in a condition or a loop, extract a new component and put it there.

See Here.

Passing Data With useState

import { useState } from 'react';

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      Clicked {count} times
    </button>
  );
}

export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Counters that update together</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

Passing Props To Child Components in React

I am having difficulty with the concept of props passing data to child components.

One way that props are used is with components. React uses components to build out the web app. Rather than one large page, its divided into “components” that are used to construct the app. That way components can be reused.

The basic format of a component with an event handler in React is:

import * as React from 'react';
import Component from '/directory/Component';
import Component2 from '/directory/Component2;

export default function ComponentName() {
	
		//event handler
	function handleClick() {
		alert('You clicked me!');
	}
	
	const nameName = [
		'   ',
	]
	
	const [
		name,
		name2,
		]
	
	return (
	
	THIS IS WHAT YOU WANT RENDERED IN THE BROWSER
	
	);
	}

React components are regular JavaScript functions, but their names must start with a capital letter or it won’t work.

The const declaration creates block-scoped constants, much like variables declared using the let keyword. The value of a constant can’t be changed through reassignment (i.e. by using the assignment operator), and it can’t be redeclared (i.e. through a variable declaration). However, if a constant is an object or array its properties or items can be updated or removed.

See MDN Web Docs

React Beta docs explains passing props to components as follows:

React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions.

React And CSS

In React, you specify a CSS class with className. It works the same way as HTML class attribute.

Use PascalCase for the actual className.

<img className="AvatarPicture" />

Write the CSS in a separate file:

/* In your CSS */
.AvatarPicture{
  border-radius: 50%;
}

Adding Strikethrough On Text

This is from React Conditional Rendering

<del> tag will strikethrough text.

Use conditional rendering to strikethrough completed items rather than deleting altogether. This may be something that I can use in my shopping app.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✔'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Deploying A Laravel App To An Apache2 Server Running Ubuntu 20.04

I used this tutorial on LinuxOpsys.

My installation is with mysql. To get app running, after it is installed, run php artisan.

Here is Laravel’s documents on deployment. Below are some of its points.

Important in a production environment, the value for APP_DEBUG in .env must be set to FALSE.

When deploying your application to production, you should make sure that you run the config:cache Artisan command during your deployment process:

php artisan config:cache

If you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files. Once the configuration has been cached, the .env file will not be loaded and all calls to the env function for .env variables will return null.

I think I will need to learn how to use Github.

Learning Github

Github is a way to track changes to files. At this point, I’m hoping to use Github as follows:

  1. Develop Locally with VSCode.
  2. Push code to github
  3. Push code from github to server.

At this point, I have no idea what I’m doing.

Time for a Github Tutorial.

Github One Hypen Versus Hyphen

Before I begin, I want to point out that sometimes a github command uses one hyphen (-) and aometimes two hyphens (–).

One hyphen is used for abbreviated flags. Two hyphens are used for complete word flags.

Abbreviated flag

git add -a

Complete word flag

git add --all

Github Intro

So, I tried using the github extension in vscode and it was too confusing. Instead, I used the command line.

At this point, my laravel project was contained on my local computer. Let’s say it’s in /home/laravel_app.

With github, I want to track different versions. To do that,Github uses a concept called repository.

A repository contains all of your project’s files and each file’s revision history. You can discuss and manage your project’s work within the repository.

Git Hub Docs

So, to get it working a little, I created an empty repository on github that is called shopping.

Then I’ll push files from /home/laravel_app to the repository on github. The address to use to push files to Github is:

https://github.com/user_name/repository_name.git`

So for me, it will be:

https://github.com/murwell/shopping.git

Local Github Repository

Now, I have to set up a repository on my local computer. This is done in the command line.

Change into the folder of where the laravel is stored:

cd /home/laravelapp

Laravel already contains a file in root folder called .gitignore. The purpose of this file to tell git to omit certain files and folders so that they do not get pushed/pulled to github. For example, .env is included in .gitignore because it contains sensitive info.

This is the contents of laravel.gitignore.

/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.idea
/.vscode

After making sure that you app folder has a .gitignore folder, run the following command while in /home/laravel_app folder to create a hidden folder called git:

git init

After you run git init, it’ll ask you if you want to rename master. You should because the main branch on github is called main.

The local branch should be renamed to “main” with:

git branch -M main

With a -m or -M option, oldbranch will be renamed to newbranch. If oldbranch had a corresponding reflog (reference log], it is renamed to match newbranch, and a reflog entry is created to remember the branch renaming. If newbranch exists, -M must be used to force the rename to happen.

See git-scm.com

Then run the following commands and substitute your name and email address between the quotation marks (“ “).

git config --global user.name "user_name"

git config --global user.email "user@email.com"

This will add your name and email so that if you’re working with someone, you can see who made the changes.

Now, you have to add the local files to your local repository. Since you are in the folder of the laravel app, run:

git add --all                                                   

This will add all the files and folders in the directory to the local repository. It will ignore the files and folders listed in .gitignore.

These files are added but sill need to be committed. Commit will not work unless a file has been added prior to running it. (i.e. -git add <FILENAME>)

This process of adding a file is called staging. Commit comes after stage (add).

git commit creates a commit, which is like a snapshot of your repository. These commits are snapshots of your entire repository at specific times. You should make new commits often, based around logical units of change. Over time, commits should tell a story of the history of your repository and how it came to be the way that it currently is. Commits include lots of metadata in addition to the contents and message, like the author, timestamp, and more. Commits are the building blocks of “save points” within Git’s version control.

Github Docs

git commit -m "Committing initial files to the repository"   

The flag -m indicates that you’re going to add a message to the commit so you can explain why you’re taking the snapshot.

Common Commit Commands

From Github Docs:

git commit

This starts the commit process, but since it doesn’t include a -m flag for the message, your default text editor will be opened for you to create the commit message. If you haven’t configured anything, there’s a good chance this will be VI or Vim. (To get out, press esc, then :w, and then Enter. :wink:)

git commit -m  "descriptive commit message" 

This starts the commit process, and allows you to include the commit message at the same time.

git commit -am "descriptive commit message" 

In addition to including the commit message, this option allows you to skip the staging phase. The addition of -a will automatically stage any files that are already being tracked by Git (changes to files that you’ve committed before).

git commit --amend 

Replaces the most recent commit with a new commit.

git branch

Shows the branches with an asterisk next to the active branch

git branch name_of_branch

Creates branch that is called “name_of_branch”

git branch -d name_of_branch

Deletes branch if merged (only use lowercase “d” to prevent a delete of a branch that has not yet been merged)

git branch -m new_name

Renames the active branch to “new_name”

git checkout name_of_branch

Switches to the “name_of_branch”

git checkout -b new_branch

Creates a new branch with the name “new_branch” and switches to that branch.

To merge branches with main

git checkout main

Switches to main

git merge branch_to_be_merged

Merges branch to main

git branch -d branch_to_be_merged 

Deletes branch that has been merged

Adding Remote Repository

So far, I’ve been mainly working with the local repository.

I previously made a repository on Github that I want to now use to sync my local files.

The remote github repository is:

https://github.com/murwell/shopping.git

To add this remote repository info to my local repository so that the two can be synced, use:

git remote add <remote_name> <remote_url>

Traditionally, origin is used as the remote name. You can use anything. Recommend to stay with origin.

git remote add origin https://github.com/murwell/shopping.git

Now, to push the files:

git push origin main

This pushes to the main branch on my remote repository.

At this point, these two commands should work if all I’ve done is change exisiting files.

git commit -am "add message"
git push

If I’ve added a file, I should run:

git add -a
git commit -am "add message"
git push

What Is Illuminate\Http\Request;

In many controllers, there is this line: use Illuminate\Http\Request;

In the Laravel documentation, this is described as:

Laravel’s Illuminate\Http\Request class provides an object-oriented way to interact with the current HTTP request being handled by your application as well as retrieve the input, cookies, and files that were submitted with the request.

The file is found at: vendor/laravel/framework/src/Illuminate/Http/Request.php

It is recommended to browse through the contents of the file to see how it’s set up. You can see different methods that are defined in this class. The request class is the way to get the information that is being submitted by the user.

And what you’ll see it that Laravel has all of these predefined methods. And when you look at code, it may not be clear how something works. It just does because there’s an underlying method.

Introduction

When I began this journey on August 19, 2022, there was no Laravel Bootcamp. It wasn’t until after I had been painfully reading documentation for almost a month that Laravel released a tutorial that is beginner/intermediate friendly. If you’re starting on Laravel, go to Laravel Bootcamp. It may not all make sense, but it’s definitely helpful. Sure beats reading their documentation.

Much I what I am writing here is for my own benefit. I don’t even bother looking at how many people visit this blog. I don’t care. Documenting code, being able to explain concepts in a simple manner is the way I learn. If I can’t explain something, then I know nothing.

This documentation will be a work in progress for months. In the movie Mank, the screenwriter of Citizen Kane is criticized for turning in a very long screenplay. His response, “If I had more time, I could make it much shorter.”

With that in mind, I hope that this will eventually become concise. But I make no promises. I’m no Mank.

Why I Want To Learn About CRUD Apps

I run this genealogy website based on open source software called webtrees. The people behind the software are brilliant. The software works so well that if you’re putting up a genealogy website, webtrees is it.

While I working on my genealogy website, I became interested in how world history impacted my family. Webtrees has a way to add a historical timeline, but I would like something more. And to do that, I decided to learn how to code a CRUD app.

CRUD stands for create, read, update, delete. The basic functions that is needed for every database.

To learn, I’m going to create a shopping list app. That’s about as simple of a CRUD app as I could envision. But, it would give me the basics so that one day I will have this enormous website that will have beautiful timelines and maps to explain my family.

Before beginning this project, I had little familiarity with databases. I’ve worked with spreadsheets. Put up wordpress websites that used MYSQL. And even used MongoDB, a non-relational database. But the reality is I didn’t understand how a database worked. There are enough tutorials out there that one can get by just copying and pasting. Something like plagiarizing a term paper off of wikipedia.

This time around I figured I should understand a little more.

Choosing Laravel

I knew enough that I couldn’t just bang out some CRUD app without lots of help.

Years ago, I put up static websites. That’s when the website basically stays the same. There’s some html, some js, no real database. You might store some photos in an image directory. It’s just text and photos.

But even with a static website, I used a framework to make it easier. In [Bootstrap}(https://getbootstrap.com/) and other similar frameworks, smart people develop shortcuts to make coding a website faster and better. Rather than having to design a button or a table, there are some words you type in and you’re done. Or you can even just download a template, add in your texts and images, and it looks like you know what you’re doing.

After static websites, I wanted to do something more interactive. I then put up some websites using a framework called MeteorJs.

I didn’t know what I was doing, but I knew how about ctrl + c. Eventually, I got a CRUD app running. Although, I didn’t even know the term CRUD back then.

This time around, I knew I needed lots of help and wanted to find a complete framework to build my Crud app. When I say complete, I wanted something that would take care of the backend (the database) to the frontend (the pretty stuff on the webpage).

Now, MeteorJS is still around. But it doesn’t seem like it’s that popular any more. It also uses MongoDB which is a document-oriented database. And even though I’m not sure how much difference it would actually make, I decided I wanted a more traditional relational database. And so, I skipped going back to Meteor.

Laravel is a full framework that works with many of the most popular relational databases. It has built-in user authentication. And is actively developed.

And so, I went for dinner at the restaurant that has line outside of the door.

MYSQL

While Laravel is considered full framework, it’s actually comprised of different components. The first component to choose is the database.

I went with MYSQL because when you spin up a server on a site like DigitalOcean or AWS, MYSQL is usually one of the default configurations. But for my purposes, any of the supported databases would work.

Frontend Choice - Blade, Vue, or React

Choosing MYSQL for backend took about ten seconds. The frontend was much more complicated.

Laravel has a frontend called Blade. I’m sure it works fine. But from my past experience, I wanted something that is very popular. It makes it easier when creating a website to copy/modify someone elses work rather than starting from nothing. I’m not saying that Blade isn’t well supported. But in my cursory review, it looked like VUE and React were much more polished.

There’s also something called limewire which is described on their homepage as “a full-stack framework for Laravel that makes building dynamic interfaces simple, without leaving the comfort of Laravel.”

Limewire sounds great. I looked into it briefly. But again decided to go with a more popular choice. Although, I don’t know the actual popularity of the different Laravel combos: Laravel/Blade, Laravel/Limewire, Laravel/VUE, Laravel/React, Laravel/Next, etc.

It seems like choosing a front end is like deciding on an ice cream flavor at Baskin Robbins. 31 choices are just too many.

Anyway, I had to make a choice. So, I decided to go with VUE because it was supposedly easier to learn than React.

Using VUE With Laravel Means Inertia

When I first chose to go with VUE (and later React), I didn’t understand that a consequence would be InertiaJs.

Inertia describes itself “as glue that connects [Laravel to React].”

I don’t know if its the glue. But for someone with little experience, it has made learning Laravel a lot messier.

Inertia complicates Laravel, in part, because much of the Laravel documentation is written for Blade. And Blade does not use Inertia.

Also the Laravel documentation isn’t particularly clear on Inertia’s importance. They don’t hide the connection. But all of these little bits and pieces need to be looked at because of Inertia.

  1. Laravel Inertia Discussion
  2. Inertia Website
  3. Laravel - Vite Website

Oh, yeah, there’s also something called vitejs which laravel describes as an asset bundler.

This splatter of information is also compounded because much of the documentation is lacking. Someone with more experience would likely understand it, but for me it’s close to hieroglyphics.

All this junk just to write a shopping list app. Pen and paper is looking pretty good at this point.

End Of Introduction

Overview of Laravel

There are five basic areas of using Laravel: the Model, the Migrations, the Controller, The Routing, the Views.

But before even getting there, I need to discuss the relational database.

A database is divided into tables. Each table represents a type of informations. In my shopping list app, I have two tables: Users and Items. In Laravel, whenever you create a table, some columns are automatically created: id, created_at, updated_at.

Here’s how the Users Table Looks:

Users Table

Here’s how the Items Table Looks:

Photo Of Items Table

Each table is divided into columns that hold a specific type of data. The fields are the name of the columns. And every time an entry is made in the database, a row is filled in the table.

Mysql tables are setup similar to the the sheets of spreadsheet.

Spreadsheet Example

For my frontend, I liked VUE. I played around with for about a week or two.

But going back to my concept of eventually building a historical timeline, I switched to React.

The reason is because I came across React-Chrono, a react timeline component.

And so for my ultimate purpose, building a timeline, I am using Laravel with MYSQL as the database and React as the frontend.

Off we go.

Creating Gates & Policies

Gates

Gates are simply closures that determine if a user is authorized to perform a given action.

Gates are written in the boot function at:

app/Providers/AuthServiceProvider.php

Below is an example where a method destroy-item is defined. It returns a boolean on whether the user is the same user who created the post.

public function boot()
{
    $this->registerPolicies();
 
    Gate::define('destroy-item', function (User $user, Item $item) {
        return $user->id === $item->user_id;
    });
}

Then in the ItemController, you include the defined gate with use Illuminate\Support\Facades\Gate; and in the destroy function you add an if statement:

public function destroy($id)
	{
        $item = Item::findOrFail($id);
        if (! Gate::allows('destroy-item', $item)) {
            abort(403);
        }
        $item->delete();
        return redirect('/items')->with('completed', 'Item has been deleted!');
	}

With the if statement, you can check if the gate is working by trying it with and without the inequality operator (exclamation mark - ! - ).

Policies

I had difficulty getting this working. Mainly, it because I had written “destroy” instead of “delete” in a method.

I modified the example from the Laravel Bootcamp. But even with that, I had written the wrong word and wound up wasting too much time.

Laravel policies are classes that organize authorization logic around a particular model or resource. The idea being that you create policies that limit what an authorized user can do. For example, the authorized user should only be allowed to modify or delete the items that particular user created.

Immediately below is what I had gotten out of the documentation from Laravel. The bootcamp is more to the point.

/**
     * Determine whether the user can update the model.
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Item  $item
     * @return \Illuminate\Auth\Access\Response|bool
     */
    public function update(User $user, Item $item)
    {
        //
            return $user->id === $item->user_id;
            
    }

    /**
     * Determine whether the user can delete the model.
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Item  $item
     * @return \Illuminate\Auth\Access\Response|bool
     */
    public function delete(User $user, Item $item)
    {
        //
           return $user->id === $item->user_id;
    }

The above functions will return true if the current user’s id (i.e. - id) is the same as the user who created the item (i.e. - user_id was stored in the items table and is the id of the person who created the item).

A policy may be generated using php artisan make:policy ItemPolicy. The generated policy will be placed in the app/Policies directory. If this directory does not exist, Laravel will create it.

After running the artisan make:policy, the following file is created in app/Policies/ItemPolicy.php.

<?php

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class ItemPolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }
}

By adding a model option, php artisan make:policy ItemPolicy --model=Item, a class ItemPolicy will be generated with sample methods in app/Policies/ItemPolicy.php. The methods will have to be filled in.

The Resource Controller methods relate to the policies as follows:

Controller Method Policy Method
index viewAny
show view
create create
store create
edit update
update update
destroy delete

Each of the methods in the policy file below return a boolean. **When requests are routed to the given controller method, the corresponding policy method will automatically be invoked before the controller method is executed. **

Each of the methods in the policy file below return a boolean.

@return \Illuminate\Auth\Access\Response|bool.

bool is a boolean. A bool expresses a truth value. It can be either true or false.

In the Laravel Bootcamp, the policy is called into the controller like this.

$this->authorize('update', $item);

Once ‘$this->authorize( controllerMethod,$__ )’ is placed in the controller method, it blocks all users.

Then in the policy file, the following is inserted:

public function update(User $user, Item $item)
    {
        return $item->user()->is($user);
    }

This would authorize th euser who created the item to modify it.

In the bootcamp, it is also suggested to create the delete policy method with:

 public function delete(User $user, Item $item)
    {
        return $this->update($user, $item);
    }

This makes the same authorization as the update method.

And in the ItemController method or destroy, the following line of code is inserted:

$this->authorize('delete', $item);

That is same format as used in update method of the controller.

The error I had made was to write the ItemController method with $this->authorize('destroy', $item);. That was WRONG because the function in the policy is called ‘delete’. Not destroy.

Remember!!!

Controller Method Policy Method
index viewAny
show view
create create
store create
edit update
update update
destroy delete

Eloquent Eager Loading Shortcut To Address N+1 Problem

For me, someone who is just trying to get a simple CRUD app to work, this is a pretty advance topic. Eager loading versus lazy loading has to do with making a query more efficient.

The purpose is to address the “N + 1” problem that exists in relational databases. I had never heard of this issue before which isn’t surprising given my basic knowledge.

But here’s an article from Kevin Gilpon of appmap explaining this problem.

Laravel’s solution is using the eager loading with method.

This does not to be appear a problem with my simple shopping list app. But it’s definitely something to keep in mind where you will be returning multiple results from multiple tables.

Laravel’s example is a list of all books and their authors from two tables: books and authors with a Book model that “belongs to” an Author model.

The with method allows you to specify which relationships should be eager loaded. That means related data is loaded at the time of the query. Here the author data is eager loaded.

$books = Book::with('author')->get();
 
foreach ($books as $book) {
    echo $book->author->name;
}

Database Relations - And you thought your family was complicated

Another area that caused me frustration was matching the creator to the items they added.

I’ve worked with databases before and was familiar with the concept of adding an identifier from one table to another so that that the two tables are related.

Generally, as Microsoft points out, a relationship defines how two entities relate to each other. In a relational database, this is represented by a foreign key constraint.

Adding Foreign Key To Table

MYSQL is a relational database and it’s oftentimes necessary to relate one table to another.

In a shopping list app where different users insert different data into the table, keeping track of who is inserting what is important so that when the list is shown only the items that were added by the actual creator are visible.

It’s too complicated to just keep adding columns to tables. It’s better to separate tables into natural divisions.

Laravel automatically seta up a users table that will track the name, email, etc. And every record (row) of the table will have a distinctive id.

In fact, every table that is created will have an id field (column ). And it is that id that can be referenced in a different table. That way you can connect tables to each other and be able to query them easier.

In my shopping list app, I currently have two tables: users and items.

Everytime a user registers, a new record (row) is made in the users table. And, of course, whenever a new item is added, a new record is added to the items table.

By adding a field (column) into items that tracks the user who created the item, the shopping list can be easily individualized. In other words, no one needs to know how much I love junk food.

To do this, there’s this concept of a foreign key.

And in Laravel, there are different ways to create a foreign key. They all involve adding schema in a migration file.

Here’s the schema from the items table migration file.

    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name');
            $table->bigInteger('user_id')->nullable()->unsigned();
            $table->foreign('user_id')->references('id')->on('users');
        });
    }

The last two lines link the “id” column from the “users” table to a column (“user_id”) in the “items” table.

$table->bigInteger('user_id')->nullable()->unsigned(); creates the user_id column that will store the user’s id.

$table->foreign('user_id')->references('id')->on('users'); establishes the link between “user_id” in the “items” table and “id” in the “users” table.

There’s also shorter code that combines the above last two lines. It uses Laravel’s foreignId() method.

Schema::table('items', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained();
});

The foreignId method creates an UNSIGNED BIGINT equivalent column, while the constrained method will use conventions to determine the table and column name being referenced. If your table name does not match Laravel’s conventions, you may specify the table name by passing it as an argument to the constrained method:

Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained('users');
});

Once the foreignId column (“user_id”) has been established, it must be referenced in the appropriate table controller to store the appropriate data.

In my shopping app, it would be referenced in the ItemController.php.


namespace App\Http\Controllers;
use Auth;
use Illuminate\Http\Request;
use App\Models\Item;
use Inertia\Inertia;

class ItemController extends Controller
{
	/**
 	* Store a newly created resource in storage.
 	*
 	* @param  \Illuminate\Http\Request  $request
 	* @return \Illuminate\Http\Response
 	*/
	public function store(Request $request)
	{
        $validated = $request->validate([
            'name' => 'required', 'max:50',
        ]);

        $item = new Item;

        $item->name = $request->name;

        $item->user_id = Auth::id();

        $item->save();

        return redirect('/items')->with('completed', 'Item has been saved!');
    }
}

use Auth; has to be added to the top of the file because Auth is called at $item->user_id = Auth::id();.

The store function will add a new item to the items table. Here are the columns.

Columns Of The Items Table

If there was another table, let’s call it “shops”, that could be incorporated like this:

Schema::create('items', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name');
            $table->bigInteger('user_id')->nullable()->unsigned();
            $table->foreign('user_id')->references('id')->on('users');
            $table->bigInteger('shop_id')->nullable()->unsigned();
            $table->foreign('shop_id')->references('id')->on('shops');
        });

Then, we would need to add another line to store function in the controller.

Of course, we would also need to add a use statement to the top of the controller. use App\Models\Shop; which would make the shop table available to the controller.

public function store(Request $request)
	{
        $validated = $request->validate([
            'name' => 'required', 'max:50',
        ]);

        $item = new Item;

        $item->name = $request->name;

        $item->user_id = Auth::id();
        
        $item->shop_id = Shop::id();

        $item->save();

        return redirect('/items')->with('completed', 'Item has been saved!');
    }

HasMany Relationship

Laravel has another way to accomplish the above. And this is done by establishing relation methods in the appropriate Models.

Relational databases has specific terms to describe the relationship between different tables.

Here’s an article on database tutorials from dotnettutorials.

One to Many is the most common database relationship. For example, in my shopping list app, one user has a relationship to many items. Of course, there is also an inverse relationship where certain items belong to only one user.

In Laravel eloquent, a one to many is called HasMany. And the inverse relationship where the many items belonging to one user is called belongsTo.

Once these relationships are defined, Eloquent makes it easier to retrieve the appropriate records.

Previously, this is how I created the store function:

public function store(Request $request)
	{
        $validated = $request->validate([
            'name' => ['required', 'max:20'],
        ]);
        //creates an object item from the Class Item
        $item = new Item;
        //name is the property of $item and its value is assigned from the request
        $item->name = $request->name;
        // Stores id of user  into user_id
        $item->user_id = Auth::id();

        $item->save();

        return Redirect::route('items.index');
    }

Instead, in the bootcamp tutorial, it is recommended:

public function store(Request $request)
    {
        //
        $validated = $request->validate([
            'name' => 'required|string|max:25',
        ]);
 
        $request->user()->items()->create($validated);
 
        return redirect(route('items.index'));
    }

The first and last statements are basically the same as in the store function I created.

It’s the middle part that is totally different.

In the bootcamp version, the middle statement is creating a record that will belong to the logged in user by leveraging an items relationship.

items() is a hasMany method that must be created in the User Model at app/Models/User.php.

The eloquent create method accepts an array of attributes, creates a model, and inserts it into the database. The difference between save and create is that save accepts a full Eloquent model instance while create accepts a plain PHP array.

<?php
 ...
class User extends Authenticatable
{

public function items()
    {
        return $this->hasMany(Item::class);
    }

This items() method defines a hasMany relationship.

And once that relationship is established, Laravel’s Eloquent will automatically determine the proper foreign key column for the Item model. By convention, Eloquent will take the “snake case” name of the parent model (User here) and suffix it with _id. So, in this example, Eloquent will assume the foreign key column on the Item model is user_id.

This will allow Eloquent to find all of the user’s items.

Then to find the user from the item, the inverse relationship must also be created. It is called belongsTo.

 <?php
 ...
class Item extends Model
{
 ...
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Once these relations are created, the store function works likes this:

 Public function store(Request $request)
    {
        //
        $validated = $request->validate([
            'name' => 'required|string|max:25',
        ]);
 
        $request->user()->items()->create($validated);
 
        return redirect(route('items.index'));
    }

And the index function in Item Controller can be written like this:

 public function index()
	{
        $user = Auth::user();
        $items = Item::whereBelongsTo($user)->latest()->get();
        return Inertia::render('Index', ['items' => $items]);
	}

Before using relationships, index was written like this:

public function index()
	{
    	$items = Item::all()->sortByDesc('id')->values();
        return Inertia::render('Index', ['items' => $items]);
	}

Sort By Descending

In my shopping list app, when I added an item, it was inserted at the bottom of the table. That’s annoying because you don’t easily see that an item was added.

Eloquent has a sortbyDescending Method. When I tried using it, all I got was a blank page.

I did a search and found the answer on Github.

There are multiple solutions.

Sort with eloquent methods like these:

  1. $events = Events::orderByDesc('date')->get();
  2. $events = Events::all()->sortByDesc('date')->values();

The sorting worked after I changed the index function in my ItemController to:

public function index()
	{
    	$items = Item::all()->sortByDesc('id')->values();
        return Inertia::render('Index', ['items' => $items]);
	}

all(), sortByDesc(), values() are eloquent methods.

all() - returns the underlying array represented by the collection.

sortByDesc() sorts the collection in the opposite order from sort().

values() returns a new collection with the keys reset to consecutive integers.

Now, whenever I add an item it goes right to the top of the list.

Sort with query building

Another way to sort is with hasMany and belongsTo relationships. These are queries and are different from eloquent methods. Before these queries can be used, the methods must be created in the Model. Then these queries can be linked together with latest() query builder like this:

public function index()
	{
        $user = Auth::user();
        $items = Item::whereBelongsTo($user)->latest()->get();
        return Inertia::render('Index', ['items' => $items]);
	}

latest() does not work with all(). So,Item::all()->latest() does not work.

This is because all() is a eloquent collection method and latest() is a query builder.

Eloquent collection methods and query builders do not work together.

sortbyDesc() is an eloquent collection method so it works with all().

whereBelongsTo is a query and so latest() works with it.

This highlights one of the difficulties that I’ve had learning laravel.

One of which is the concept that there are two fundamental ways to pull data from databases in Laravel: eloquent and database queries.

And if you confuse the two (which is easy for me to do), it does not work.

A google search of Eloquent vs query builder returns numerous discussions about which to use and when. For the time being, I will try to get them separate. It may be a good exercise to use each for the same thing.

Using React On The Front End

I finally got CRUD working, and so I’m going to start working on making my shopping list app look nice.

As far as structure, I will have a homepage that’ll have a couple of cards on them. I don’t expect to have many users. Probably just me. Maybe a couple of friends will try it. But that’s about it. After finishing the shopping list app, I’ll probably switch to a recipe app. What I’m thinking is that I’ll just do it all in one database. They could even work together. So, maybe I’ll have three cards. One with Shopping list. One with registration info. And one with recipe maker coming soon.

As far as the shopping app, I’m thinking it should all be in one page. At the top, an input line with and add button. Underneath, the list of items. And next to each item, a delete button. I think I’ll leave out an edit button. I’ve used a shopping list app before and all I care about is the list. This will be the first iteration. Just to see it up and running.

At the top will be the navabar. Below the menu, the input line with an add button.

Index page, with a store component, and a destroy (delete) button.

So, the index page will be the main page.

Then I’ll have:

  1. navar component
  2. create compoent ( a form with a box and a store button.
  3. a loop component that’s list all the items and have a delete button

I think that’s it.

Some Styling

And here’s how version 1 looks:

Screen shot of shopping app version 1

Most of this uses MUI material-UI. Responsive Bar With Menu, Button, and Basic Table.

Responsive Bar With Menu

The Responsive Bar can be found here.

The source code is some serious work and this is why we use components like material-ui. I could spend the next ten years trying to come up that code, and it would never happen.

It’s all complicated, but the part that I needed to change was the * const navItems*:

import Button from '@mui/material/Button';

const drawerWidth = 240;
const navItems = ['Home', 'About', 'Contact'];

The const navItems are the menu links that’ll appear in the AppBar. I needed to change the names and add in some links. This is how I coded it:

const navItems = [
    <a href='/'>Home</a>,
    <a href={route('items.index')}>Shopping</a>,
    <a href='https://emlekezik.com/'>Emlekezik</a>
];

The first link will take you home. The second link takes you to the shopping app. And the third link is an external genealogy website that I run.

Since these are links, I tried using Inertia Links. But I couldn’t get it work. All I got was a blank page. So, I left out the Inertia Link.

Material-UI recommends using a prop called component="a" in the ListItemButton.

In this Responsive Bar, there’s a ListItemButton. So, I added the component like this:

	 <List>
        {navItems.map((item) => (
          <ListItem key={item} disablePadding>
            <ListItemButton component="a" sx={{ textAlign: 'center' }}>
              <ListItemText primary={item} />
            </ListItemButton>
          </ListItem>
        ))}
      </List>

The link worked even without this component, but I left it in because it also worked with it.

And now, on the desktop, the app looks like this:

Shopping App Desktop View

And in mobile, with the side menu open, it looks like this:

Shopping App Mobile View

And the links all work.

Life is good.

bootstrap.laravel.com

For whatever reason, laravel doesn’t advertise this tutorial atboostrap.laravel.com. I only found out about it from a posting on a forum. If I had seen it earlier, the tutorial would have saved me a couple of weeks of time.

Here are a few things I learned:

Limit the authentication to certain resource routes with ->only(['index', 'store]) like below:

Route::resource('chirps', ChirpController::class)
    ->only(['index', 'store'])
    ->middleware(['auth', 'verified']);

Use verified only if email verification has been setup.

###FrontEnd Part 2 - Typescript Here We Come

Life was good for a day. But then I began working on my homepage and it feels king of disjointed with a messy code. So, I’m going to start over on the front end with React, Mui Material-ui and typescript.

Text Bar

The text box is from Inertia useForm. I haven’t yet figured out how I can change the styling to go with Material UI.

Inertia points out that “While it’s possible to make classic form submissions with Inertia, it’s not recommended, as they cause full page reloads. Instead, it’s better to intercept form submissions and then make the request using Inertia.”

That’s something I noticed when I was playing around with the text field. When I added an item, the entire page would reload. With useForm, only an item would be added. I did change the button to the one from material-UI. But the tes field is still from

Important Stuff

Eloquent is different from query builder.

The key to Laravel fun is href={ route('items.destroy', item.id)}

1The above code, with the below function in ItemController, worked to delete an item:

public function destroy($id)
	{

        $item = Item::findOrFail($id);
        $item->delete();
        return redirect('/items');
	}

To define routes in your code, you must follow the default routes established in Laravel.

Verb URI Action Route Name
GET /items index items.index
GET /items/create create items.create
POST /items store items.store
GET /items/{item} show items.show
GET /items/{item}/edit edit items.edit
PUT/PATCH /items/{item} update items.update
DELETE /items/{item} destroy items.destroy

The route names are the way to reach the appropriate functions in the controller.

Routing is very important in Laravel. It makes everything work. This means you’ll need to get a grasp of Inertia if you’re gonna use Laravel with React or Vue.

Lttle Things Are Annoying

  1. When using Redirect in the Controller, import the Redirect class with use Illuminate\Support\Facades\Redirect; If not, you’ll get an error.

Passing Data From Laravel Controller To React Page

The basic way to show passed data in a web page is by using {props.name_of_collection.name_of_field} or for my shopping app {props.item.name}.

Here’s a more thorough explanation.

The standard Laravel Controller has a show function. The purpose of this function is to show more information about a specific item. This is used all the time on the web. You’re on Amazon and see a list of possible products. You click on an item, and it takes you to the specific item page that’ll have more info. That’s show biz.

In my shopping app, a show function is not needed. You’re just making a list. But here’s how to use the show function anyway in the shopping app.

Let’s start at the beginning. There’s a database and in the database there is a table called items that looks like this.

Photo Of Items Columns

There are five fields (ie -columns). id is the way the items are tracked. There can never be a duplicate. Whenever a new item is added, a new id is created. Even if the item is eventually deleted, the id will never be duplicated.

When the list of items is returned in the index page, the name of each item was hyperlinked with the following code:

<td><Link href={ route('items.show', item.id)}>{item.name}</Link></td>

(A map function will automatically create this link for each item.)

The route of the link is route('items.show', item.id). It will link to a show page with the appropriate id.

http://127.0.0.1:8000/items/20

The 127.0.0.1:8000 is the local server. items is the name of the table. And 32 is the id of the item. This matches the route format created by the Laravel Controller resources.

It will call the show function that was created in ItemController.php.

     public function show($id)
	{

        $item = Item::findOrFail($id);
        return Inertia::render('Show', [
                'item' => $item,
        ]);
	}

This function will take the id of the item clicked (i.e.- 20 in my example) and do two things:

First, it will render the page /resources/js/Pages/Show.jsx. Second, it will also pass the contents of the item with id 20 to the Show.jsx. Then in the page Show.jsx, you can use the data by calling it with [props.item.name], for example.

As previously noted, there are five columns (fields) in the items table:

id, created_at, updated_at, name, user_id

Any of those columns can be called in Show.jsx by using:

{props.item.column_name} or {props.item.created_at}, etc.

Per Inertia, the amount of data that is passed can be limited by passing the desired columns with only in the controller as follows:

     public function show($id)
	{

        $item = Item::findOrFail($id);
        return Inertia::render('Show', [
                'item' => $item->only('id', 'name'),
        ]);
	}

Updating

Here is the useForm code from Inertia to modify an item.

It was changed to work for my shopping app.

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'

export default function Modify(props) {
const { data, setData, patch, processing, errors } = useForm({
  name: '',
})

function submit(e) {
  e.preventDefault()
  patch(route("items.update", props.item.id));
}

return (
    <div>
  <form onSubmit={submit}>
    <input type="text" value={data.name} onChange={e => setData('name', e.target.value)} />
    <button type="submit" disabled={processing}>Submit</button>
    {errors.name && <div>{errors.name}</div>}
  </form>
  <h1>{props.item.name}</h1>
  </div>
)
}

Remember, the above code comes from Inertia and uses their helper useForm. The tricky part is the route format in the function submit(e). But before we go through to code, it’s important to understand that this code resides in the edit.jsx page.

Add so, to get to this edit page, the following link is clicked: http://127.0.0.1:8000/items/40/edit 127.0.0.1:8000 is the local server address. Items is the name of the table. 40 is the id of the speciic item. edit is the function.

This link follows the default edit route for Laravel: GET /items/{item}/edit.

And when the link is clicked, it calls the edit function in the ItemController. That function is written as:

/**
 	* Show the form for editing the specified resource.
 	*
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
	public function edit($id)
	{

        $item = Item::findOrFail($id);
        return Inertia::render('Edit', [
            'item' => $item,
        ]);
	}
***

The function causes the following to happen when the following link is clicked: http://127.0.0.1:8000/items/40/edit

The id of 40 from the link is passed to function. The findorFail Method will assign the properties of the item with id:40 to the variable $item. Inertia will render edit.jsx and receive the contents of the item with id:40.

The code of edit.jsx is:

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'

export default function Modify(props) {
const { data, setData, patch, processing, errors } = useForm({
  name: '',
})

function submit(e) {
  e.preventDefault()
  patch(route("items.update", props.item.id));
}

return (
    <div>
  <form onSubmit={submit}>
    <input type="text" value={data.name} onChange={e => setData('name', e.target.value)} />
    <button type="submit" disabled={processing}>Submit</button>
    {errors.name && <div>{errors.name}</div>}
  </form>
  <h1>{props.item.name}</h1>
  </div>
)
}

This page will receive item from the controller. And pass it in function submit(e) to the update (patch) route.

function submit(e) {
  e.preventDefault()
  patch(route("items.update", props.item.id));
}

And the function callls the Laravel update route and passes the id of 40 to the update function in the ItemController when the submit button is clicked. It will also pass in the update function the value of the textbox to the target value of name.

And the update function in ItemController is:

/**
 	* Update the specified resource in storage.
 	*
 	* @param  \Illuminate\Http\Request  $request
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/

	public function update(Request $request, $id)
	{
        $item = Item::findOrFail($id);
        $item->name = $request->name;
        $item->save();
        return Redirect::route('items.index');
	}

Here we see item receives the value of id:40 and the new value of name. The item is saved and then the pages is redirected to the index page. When using redirect in a controller, make sure to import the Redirect class with use Illuminate\Support\Facades\Redirect;

Otherwise, there will be an error.

Stuck In Laravel With ID Blues Again

I’ve been stuck for days on how to edit, show, or delete a specific item in my shopping app.

Basically, I’m abble to create an item. I can return a list of the items that are in the data, but that’s it.

This is what I envision, a list of items on my shopping list. Each item is hyperlinked and if I click on that link, it would take me to a page (show) where I could have more info about that item. On the show page there would be button that would take me back to the overall list.

On the overall list (index), there are two buttons next to each hyperlinked item. The two buttons are edit and delete,

The frustration is that the Laravel documentation makes it sound so easy:

In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well.

My issue is that I don’t know how to pass the id value of the item to the appropriate spot.

Let’s say you have an item - id: 1 name: milk. When I click on the button delete that is next to the item milk, how do I pass the id of milk to the destroy function.

Finally, figured it out.

Here Comes The Sun - Maybe

It’s been days, maybe even weeks, since I’ve been trying to delete an item from the shopping list using laravel/inertia/react.

And I just had a breakthrough.

The code to delete the item is below:

<Button variant="contained">
<Link href={ route('items.destroy', item.id)} 
method="delete" as="button" type="button" > 
Delete
</Link>
</Button>

The Button component is from MUI Material-UI.

The key is href={ route('items.destroy', item.id)}

There was discussion here that led me to the above line of code.

The above code, with the below function in ItemController worked:

public function destroy($id)
	{

        $item = Item::findOrFail($id);
        $item->delete();
        return redirect('/items')->with('completed', 'Item has been deleted!');
	}

I don’t know why Inertia doesn’t explain this in their documentation on links.

Anyway, I think I had a breakthrough. Finally.

Probably, tomorrow, inertia or laravel will change their code and it’ll be done. But for now, I’m happy.

Here’s a show link that can be clicked and take to another page:

<td><Link href={ route('items.show', item.id)}>{item.name}</Link></td>

Assuming the item has an id of 20, the url will be:

http://127.0.0.1:8000/items/20

The show function in the ItemComtroller will be called:

     public function show($id)
	{

        $item = Item::findOrFail($id);
        return Inertia::render('Show', [
                'item' => $item,
        ]);
	}

This function will take the id of the item clicked (i.e.- 20 in my example) and do two things:

First, it will render the page /resources/js/Pages/Show.jsx. Second, it will also pass the contents of the item with id 20 to the Show.jsx. In the function in Show.jsx, you can use the data by calling it with [props.item.name],for example.

items is a table in the database that is being used. In that table, there are five columns (fields):

id, created_at, updated_at, name, user_id

Any of those columns can be called in Show.jsx by using:

{props.item.column_name} or {props.item.created_at}, etc.

Per Inertia, the amount of data that is passed can be limited by passing the desired columns with only in the controller:

     public function show($id)
	{

        $item = Item::findOrFail($id);
        return Inertia::render('Show', [
                'item' => $item->only('id', 'name'),
        ]);
	}

Laravel Eloquent Methods vs Laravel Collection Methods

Another confusing thing about the Laravel documentation is the difference between Eloquent and Collections. Eloquent is a query builder that has the same methods available to it as collections. Collections are created whenever an eloquent query is run.

Since they use many of the same methods, it gets confusing.

The all method returns the underlying array represented by the collection:

collect([1, 2, 3])->all();
 
// [1, 2, 3]

Above is from Laravel Collections

The model's all method will retrieve all of the records from the model's associated database table:

use App\Models\Flight;
 
foreach (Flight::all() as $flight) {
    echo $flight->name;
}

Above is from Laravel Eloquent

Laravel says “You can think of each Eloquent model as a powerful query builder allowing you to fluently query the database table associated with the model.”

Laravel introduces its collections class as:

The Illuminate\Support\Collection class provides a fluent, convenient wrapper for working with arrays of data. For example, check out the following code. We'll use the collect helper to create a new collection instance from the array, run the strtoupper function on each element, and then remove all empty elements:

$collection = collect(['taylor', 'abigail', null])->map(function ($name) {
    return strtoupper($name);
})->reject(function ($name) {
    return empty($name);
});

As you can see, the Collection class allows you to chain its methods to perform fluent mapping and reducing of the underlying array. In general, collections are immutable, meaning every Collection method returns an entirely new Collection instance.

Almost every method returns a new Collection instance, allowing you to preserve the original copy of the collection when necessary

I’m easily confused but Laravel emphasizes that “The results of Eloquent queries are always returned as Collection instances.”

Also, “All Eloquent collections extend the base Laravel collection object; therefore, they inherit all of the powerful methods provided by the base collection class.”

Based on this, it seems that on Laravel you use eloquent first, then collection.

findOrFail and firstOrFail

In different routes I have in my app, the findOrFail methods are used to return an $id. This is a Laravel method.

The findOrFail method will retrieve the first result of the query; however, if no result is found, an Illuminate\Database\Eloquent\ModelNotFoundException will be thrown:

$flight = Flight::findOrFail(1);
 
$flight = Flight::where('legs', '>', 3)->firstOrFail();

If the ModelNotFoundException is not caught, a 404 HTTP response is automatically sent back to the client:

use App\Models\Flight;
 
Route::get('/api/flights/{id}', function ($id) {
    return Flight::findOrFail($id);
});

Form Validation

Codes that I see sometimes is in this format:

e.preventDefault() or e.target.value

This code is in a create form that I am currently using:

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'


export default function Create() {
const { data, setData, post, processing, errors } = useForm({
  name: '',
})

function submit(e) {
  e.preventDefault()
  post('/items')
}

return (
  <form onSubmit={submit}>
    <input type="text" value={data.name} onChange={e => setData('name', e.target.value)} />
    {errors.name && <div>{errors.name}</div>}
    <button type="submit" disabled={processing}>Submit</button>
  </form>
)
}

Per Mozilla:

The preventDefault() method of the Event interface tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

So, in the above code, the preventDefault function stops the uploading of the form data (post(‘/items) to the mysql database if there is an error (i.e.- the input fails the validation function).

In ItemController.php, there is the following validation rules in the store function:

public function store(Request $request)
	{

        $validated = $request->validate([
            'name' => ['required', 'max:50'],
        ]);

This means that the name field is required and cannot exceed 50 characters. If the submit button is clicked and the field is empty, nothing is sent to the database and an error message will appear: “The name field is required.”

In Laravel, form validation is discussed here.

React’s useState Hook

import React, { useState } from 'react';

In React, hooks let you use state and other React features without writing a class. useState is a hook provided by React. A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

useState declares a “state variable”. State returns a pair of values: the current state and a function that updates it. In the below example, the variable is called count but could be called anything else, like apple. Unlike with classes, the state doesn’t have to be an object. This is a way to “preserve” some values between the function calls — useState is a new way to use the exact same capabilities that this.state provides in a class. Normally, variables “disappear” when the function exits but state variables are preserved by React.

From Reactjs:

1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(0);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }

When we want to display the current count in a class, we read this.state.count:

<p>You clicked {this.state.count} times</p>

In a function, we can use count directly:

<p>You clicked {count} times</p>

useState uses a square bracket to declare the variable and function.

const [fruit, setFruit] = useState('banana');

The square bracket in JavaScript syntax is called “array destructuring”.

useState returns an array with two items. The first item is the current value, and the second is a function that lets us update it. Using [0] and [1] to access them is a bit confusing because they have a specific meaning. This is why array destructuring is used instead.

Creating A REACT Table To Return Data

Using Props To Pass Data From Controller To View

The Index function from Controller at app/Http/Controllers/ItemController.php is:

<?php

namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Models\Item;
use Inertia\Inertia;
use Illuminate\Support\Facades\Redirect;


class ItemController extends Controller
{
	/**
 	* Display a listing of the resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
     public function index()
     {
        //get authenticated user who's making request via the Auth facade's user method
        $user = Auth::user();
        // gets the items that belongs to the authenticated user and sorts by latest
         $items = Item::whereBelongsTo($user)->latest()->get();
         //renders the index page and passes the prop 'items' that contains items attributes
         return Inertia::render('Index', ['items' => $items]);
     }

From the last line, we have return Inertia::render('Index', ['items' => $items]);

That code renders the index page and passes the prop ‘items’ that contains the items attributes. It needs to be read into the react page as props.items. The function in the receiving page must include “props” as a parameter (i.e. - function nameExample (props)).

In the index page at resources/js/Pages/Index.jsx, we have placed props.items in the following table:

<TableBody>
        {props.items.map((item, index) => (
          <TableRow
          key={item.id}
          sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
          data-index={index}>
            <TableCell>{item.name}</TableCell>
            <TableCell align="left">
                <Button variant="outlined"><Link href={route('items.destroy', item.id)} method="delete" as="button" type="button" >Delete</Link></Button>
             </TableCell>
          </TableRow>
        ))}
</TableBody>

The props have now been passed to the view. The pass is complete. Touchdown!!!

Using Inertia Helper usePage() To Return Data

usePage ahould NOT be used to pass props. Below was a mistake. But it’s being left in as a reminder of what not to do. And also when to actually use this inertia method.

To return the data that is the table, we use the index function in the appropriate controller.

	/**
 	* Display a listing of the resource.
 	*
 	* @return \Illuminate\Http\Response
 	*/
	public function index()
	{
    	$items = Item::all();
        return Inertia::render('Index', ['items' => $items]);
	}

On the first line, the double colon, is a php token that allows access to static, constant, and overridden properties or methods of a class.

Here, Item:all() is a Laravel eloquent method that returns all of the results in the Item Model’s Table. These results are assigned to the variable items. This is different than the all() method for collections which returns the underlying array represented by the collection.

On the second line, Inertia is used to render the file Index.jsx and to pass the single prop itemsto the rendered file resources/js/Pages/Index.jsx.

To reach this prop on the react page, I use the hook from inertia that is called usepage. USEPAGE SHOULD NOT TO BE USED TO PASS PROPS. This is a mistake I made when I began learning react. I had read the shared data section on Inertia and used the usepage method to pass props. Wrong. Usepage is only used for data that is to be shared among different pages. Instead, it should only be used in the circumstances as described by Inertia:

Sometimes you need to access certain data on numerous pages within your application. For example, a common use-case for this is showing the current user in the site header. Passing this data manually in each response isn’t practical. In these situations shared data can be useful.

import {usePage} from '@inertiajs/inertia-react'; into Index.jsx.

The recommended way to use this hook from inertia is:

import { usePage } from '@inertiajs/inertia-react'

export default function Layout({ children }) {
  const { auth } = usePage().props

  return (
    <main>
      <header>
        You are logged in as: {auth.user.name}
      </header>
      <content>
        {children}
      </content>
    </main>
  )
}

In my app, the shared server data itemsis used liked this:

import React from 'react';
import { Inertia } from "@inertiajs/inertia";
import { usePage} from '@inertiajs/inertia-react';

function TestLoop() {
    const { items } = usePage().props
  return (
    <div className="container">
        <h1>TestLoop</h1>
        <table className="table table-bordered">
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
            {items.map((item, index) => (
              <tr data-index={index}>
                <td>{item.id}</td>
                <td>{item.name}</td>
              </tr>
            ))}

        </table>

    </div>
  );
}

export default TestLoop;

Once the data items is brought into the react page, the map function is used to list the rows of the table. Here, id and name are placed into a html table and all the rows will be returned.

Creating The Show Function With Inertia In Controller

The show function in the controller is a GET function that is used to show just one entry. Index function is used to show all entries.

The classic way is use show would be a list of items (ie. - books, movies, some collection. Then when you click on one of those items, you can more info about that specific item. Probably not that useful in a shopping list app. But can still be used as an example.

The route name is items.show.

The uri is /items/{item}.

Inertia has an example of a show function.

In the below example we’re passing a single prop, called event, which contains four attributes (id, title, start_date and description) to the Event/Show page component.

use Inertia\Inertia;

class EventsController extends Controller
{
    /**
 	* Display the specified resource.
 	*
 	* @param  int  $id
 	* @return \Illuminate\Http\Response
 	*/
    public function show(Event $event)
    {
        return Inertia::render('Event/Show', [
            'event' => $event->only('id', 'title', 'start_date', 'description'),
        ]);

        // Alternatively, you can use the inertia() helper
        return inertia('Event/Show', [
            'event' => $event->only('id', 'title', 'start_date', 'description'),
        ]);

    }
}

In my app, I’m going to pass a few attributes as a test.

My proposed controller function is:

 public function show(Items $items)
    {
        return Inertia::render('Items/Show', [
            'items' => $item->only('id', 'name', 'user_id', 'created_at'),
        ]);

    }

I just now need to figure out how to write a react page items/show that will render the data. Basically, if I am using show, should there be a way to limit the response.

Using Inertia Helper useForm() to Return Data

?

MYSQL Commands

mysql - begins terminal monitor

mysql -u<username> -p - switch mysql user

use database_name; - switches to database in monitor

show columns - after switching to specific database with the above command

show column_name from table_name; - show column

select column_name from table_name; - show contents of column

show * from table_name; - show all from column

Inertia Forms

It took me days to figure out how to use Laravel/Inertia/React to store data to MYSQL.

One stupid problem that wasted about two hours was I started npm incorrectly. I kept typing npm run -dev. And because of this stupidity, my pages were not rendering. After two hours, I reread this document and noticed that I should be using npm run dev. There is no -.

Then I spent way too many hours trying to figure out how to put together a form that will submit the text field to the database when I click a button.

The answer is simple. You have to use the format required by inertia.

I changed their recommended code to work with my Items table that has a column “name” where I will list the names of the items on my shopping list.

import { Inertia } from '@inertiajs/inertia'
import React, { useState } from 'react'

export default function Edit() {
  const [values, setValues] = useState({
    name: "",
  })

  function handleChange(e) {
    const key = e.target.id;
    const value = e.target.value
    setValues(values => ({
        ...values,
        [key]: value,
    }))
  }

  function handleSubmit(e) {
    e.preventDefault()
    Inertia.post('/items', values)
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Item Name:</label>
      <input id="name" value={values.name} onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  )
}

I don’t understand every nuance of this code. But it worked. The only other change has to be with the store function of the controller. In my case, it’s
ItemController.php.

class ItemController extends Controller
{
    public function index()
    {
        return Inertia::render('Items/Index', [
          'items' => Item::all(),
        ]);
    }

    public function store()
	{
    $validated = $request->validate([
            'name' => 'required', 'max:50',
        ]);

        Item::create($request->all());


        return redirect('/items');
	}
}

The validation in the above code is different than how Inertia wrote it. Instead, the validation came from Laravel.

Other code that worked in the ItemController.php to store the data is:

public function store(Request $request)
	{
        Item::create($request->all());

        return redirect()->route('items.index');
       
    }

And

public function store(Request $request)
	{
        $item = new Item;

        $item->name = $request->name;

        $item->save();

        return redirect('/items')->with('completed', 'Item has been saved!');

    }

The last code came from Laravel at eloquent/inserts.

Inertia Forms And The Inertia Helper “UseForm”

The above form worked to submit the data to a database. As Inertia notes:

Since working with forms is so common, Inertia comes with a form helper (useForm) designed to help reduce the amount of boilerplate needed for typical forms.

What I have noticed is that many tutorials out there just copy the below helper without noting it comes from Inertia. And it makes the writer of the tutorial seem much smarter than they are. After looking at many tutorials, most are just copies of each other without any real value added.

Anyway, below is from Inertia.

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'

const { data, setData, post, processing, errors } = useForm({
  email: '',
  password: '',
  remember: false,
})

function submit(e) {
  e.preventDefault()
  post('/login')
}

return (
  <form onSubmit={submit}>
    <input type="text" value={data.email} onChange={e => setData('email', e.target.value)} />
    {errors.email && <div>{errors.email}</div>}
    <input type="password" value={data.password} onChange={e => setData('password', e.target.value)} />
    {errors.password && <div>{errors.password}</div>}
    <input type="checkbox" checked={data.remember} onChange={e => setData('remember', e.target.checked)} /> Remember Me
    <button type="submit" disabled={processing}>Login</button>
  </form>
)

To submit the form, use the get, post, put, patch and delete methods. (I have no idea what this means. I don’t know why documentation doesn’t take it all the way home and show us the next step). I assume this means that we use the above as a template and they

const { submit, get, post, put, patch, delete: destroy } = useForm({ ... })

submit(method, url, options)
get(url, options)
post(url, options)
put(url, options)
patch(url, options)
destroy(url, options)

This is the code that I used to create an item in the database:

import { Inertia } from '@inertiajs/inertia'
import React, { useState } from 'react'

export default function Edit() {
  const [values, setValues] = useState({
    name: "",
  })

  function handleChange(e) {
    const key = e.target.id;
    const value = e.target.value
    setValues(values => ({
        ...values,
        [key]: value,
    }))
  }

  function handleSubmit(e) {
    e.preventDefault()
    Inertia.post('/items', values)
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Item name:</label>
      <input id="name" value={values.name} onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  )
}

Using the helper, the code turns into this:

import React from 'react'
import { useForm } from '@inertiajs/inertia-react'


export default function Edit() {
const { data, setData, post, processing, errors } = useForm({
  name: '',
})

function submit(e) {
  e.preventDefault()
  post('/items')
}

return (
  <form onSubmit={submit}>
    <input type="text" value={data.name} onChange={e => setData('name', e.target.value)} />
    {errors.name && <div>{errors.name}</div>}
    <button type="submit" disabled={processing}>Submit</button>
  </form>
)
}

And it worked. Of course, I don’t know how the inertia helper works. Just that it does. That’s one of the issues I’ve had in understanding all this stuff. Much of the code is hidden behind helpers, methods, classes, etc. And so, when you look at the code, it doesn’t make much sense. It just works.

PHP

A class is a template for an object. It is made up of properties (variables) and methods (functions).

Classes are extensively used in Laravel.

A Model of Laravel creates a class that is then used to create objects in controllers to interact with the database.

The basic process involving a class is:

  1. Define a class that has properties and methods.
  2. Create an object (instant) from the class. The object will have the generic properties and methods of the class.
  3. Assign specific values to the properties of the object or call a method from the object.

A class is defined by using the class keyword, followed by the name of the class and a pair of curly braces { }. All of the class properties and methods go inside the braces. In a class, variables are called properties and functions are called methods. Default values can be set.

To define a class:

<?php
class Fruit {
  // Properties
  public $name;
  public $color = 'default value';

  // Methods
  function set_name($name) {
    $this->name = $name;
  }
  function get_name() {
    return $this->name;
  }
}
?>

Then to create an object from the class that was previously defined, the keyword new is used:

$apple = new Fruit(); 

When the object is first created, it only has the generic properties and methods of the class. The single arrow operator, ->, is used to assign specific properties to the object or to call a method from the object.

Single Arrow Operator And Classes

The single arrow, ->, is an object operator and is used to access the properties and methods of the object. It can be used to create (instantiate) the actual values of the properties.

$apple->set_name('Apple'); //A method (function) of the object is used to set a property.

$apple->name = 'Apple'; // The property 'name' is assigned the value 'Apple'.

The term to the left of the single arrow is the name of the object that had been previously instantiated (created) from a class. On the point of the arrow is the property or function of the object.

Double Arrow Operator - From StackOverFlow

The double arrow operator, =>, is used for access to arrays. This means that what is on the left side will have the value of what is on the right side. It can be used to set values of an array.

$myArray = array(
    0 => 'Big',
    1 => 'Small',
    2 => 'Up',
    3 => 'Down'
);

In javascript, the => is used to define an arrow function.

const square2 = x => x * x;

It’s been more than a week since I last added to these notes. It will eventually need some serious editing.

I’ve been spending the last ten days or so refreshing my memory of javascript. I switched from learning VUE to learning REACT. The reason I did is because I want to eventually build aan extensive timeline that incorporates family history into a timeline with world events/mapping. Webtrees does it some extent. But, I’m envision something more. I came across React Chrono and it seems that it would be very useful for my purposes.

Anyway, that meant learning React. When I began looking at React, the javascript notation was confusing me. I had studied javascript years ago but had forgotten much of it. To get it back, I read over Eloquent Javascript. It’s a great learning manual and is available for free. Many of my examples are lifted from this manual. As noted in the manual: Licensed under a Creative Commons attribution-noncommercial license. All code in this book may also be considered licensed under an MIT license. Much of the below javascript info is taken from this manual

Curried Arrow Functions

Here’s an example from an accordian component:

const handleChange = (panel) => (event, isExpanded) => {
    setExpanded(isExpanded ? panel : false);
  };

Currying arrow functions takes multiple arguments (parameters) and splits them into a sequence of functions that each take an individual argument. See CodeKirei.

This is very complicated to me. Don’t really understand it.

Some Javascript Notation To Know

const square = function(x) {
  return x * x;
};
function square(x) {
  return x * x;
}

React seems to use a lot of arrow functions.

const square1 = (x) => { return x * x; };

const square2 = x => x * x;

No parameters arrow function

const horn = () => { 
	console.log("Toot"):
 };

Creating An Object

Let day1 = {
 squirrel: false,
 events: ["work", "touched tree", "pizza", "running"]
};

Inside the braces, there is a list of properties separated by commas. Each property has a name followed by a colon and a value. This means that braces have two meanings in JavaScript. At the start of a statement, they start a block of statements. In any other position, they describe an object.

Arrays, then, are just a kind of object specialized for storing sequences of things.

Destructuring Assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

This is a complicated concept for me.

Below are a few examples from mdn web docs. To see more.

Array Destructuring [ ] = [ ]

let a, b, rest;
[a, b] = [10, 20];

console.log(a);
// expected output: 10

console.log(b);
// expected output: 20

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest);
// expected output: Array [30,40,50]

Object Destructuring - const { } =

const user = {
  id: 42,
  isVerified: true,
};

const { id, isVerified } = user;

console.log(id); // 42
console.log(isVerified); // true

Repeat Function

	function repeat(n, action) {
	  for (let i = 0; i < n; i++) {
	    action(i);
	  }
	}

Higher-Order Functions

Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions.

Higher-Order Methods For Arrays

Arrays provide a number of useful higher-order methods. You can use forEach to loop over the elements in an array. The filter method returns a new array containing only the elements that pass the predicate function. Transforming an array by putting each element through a function is done with map. You can use reduce to combine all the elements in an array into a single value. The some method tests whether any element matches a given predicate function. And findIndex finds the position of the first element that matches a predicate.

The difference between forEach() and map is that they both will call a function on each element in the array but map returns a new array and forEach() does NOT return anything.

Use forEach() to add the elements of an array to a database. Use map to create a new array.

forEach() example

let sum = 0;
const numbers = [16, 77, 8];
numbers.forEach(myFunction);

function myFunction(item) {
  sum += item;
}

The above example uses forEach to sum up the numbers in the array.

Map Example

const originalArray = [16, 77, 8];
const newArray = originalArray.map(NameOfFunctionToBeUsed)

function NameOfFunctionToBeUsed(elementsFromArray) {
  return elementsFromArray * 5;
}

This uses map to multiply the numbers in the array by 5 and place them into a new array.

React

From what I’ve learned in the last week, React’s general format in Laravel is:

  1. import statements
  2. functions
  3. export default function statement
import React from 'react';
import ScopedCssBaseline from '@mui/material/ScopedCssBaseline';
import DrawerAppBar4 from './Components/DrawerAppBar4';
import DrawerAppBar4a from './Components/DrawerAppBar4a';
import { Link, Head } from '@inertiajs/inertia-react';

const Murwell = function (x,y) {
    return x*y
    };

    const symp = ["hi","bye"];

    const MenuHappy = (g) => symp.at(g);

    const Peace = (b) => b*8;

	const Choice = function choice (c) {

		if (c === 3) {
  		return ('Yay, I love chocolate ice cream!');
		}
	else {
  		return ('Awwww, but chocolate is my favorite…');
	}
	}

export default function Welcome(props) {
    return (
        <>
            <Head title="Welcome" />
                <div >
                    {props.auth.user ? (
                        <DrawerAppBar4a></DrawerAppBar4a>
                    ) : (
                        <>
                            <DrawerAppBar4></DrawerAppBar4>
                        </>
                    )}
                </div>
<h1>hi</h1>

<h1>{Murwell(3,4)}</h1>

<h1>{MenuHappy(0)}</h1>

<h1>{Peace(6)}</h1>

<h1>{Choice(4)}</h1>
        </>
    );
}

This block of code is stored in Laravel at resources/js/Pages/Index.jsx.

Some components are stored at resources/js/Pages/Components.

A library of React components was added from Miu Material UI. It was installed by running npm install @mui/material @emotion/react @emotion/styled.

To use the components, I’d copy an example component from the Miu website and insert the code into a file at resources/js/Pages/Components.

For example, for a checkbox, I’d copy an example into a file I create at resources/js/Pages/Components/CheckBoxMaterial.jsx.

To use that component, I’d first import it into a file with the following code: import CheckBoxMaterial from './Components/CheckBoxMaterial';

Then to use it, I can write <CheckBoxMaterial></CheckBoxMaterial>.

Much of the code above is where I was just practicing writing functions and importing it into the page.

One more complicated section is:

<div >
{props.auth.user ? (
	<DrawerAppBar4a></DrawerAppBar4a>
		) : (
			<>
	<DrawerAppBar4></DrawerAppBar4>
			</>
	)}
</div>

Above a ternary operator is used to return different menus depending on whether the user is logged in.

I’m going to start with a shopping-list app based off of Laravel/React.

To Install Laravel, go here.

Mysql also needs to be installed. Here are the Mac instructions for mysql.

Beginning With Vue.js

One of the many difficulties that I’ve had is figuring out how to create the frontend using Vue.

In the old days when I was doing simple static websites, I’d go to the bootstrap toolkit. Then load the css and js into an html template via cdn, copy a basic template, and I’d be off and running. Like this from Bootstrap.

<!doctype html>
<html lang="en">
  	  <head>
	    <meta charset="utf-8">
	    <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Bootstrap demo</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
  	  </head>
  	  <body>
	    <h1>Hello, world!</h1>
	    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
  	  </body>
</html>

Much of my difficulty with Laravel/React is because of my lack of knowledge (duh). And so, I’ve spent the last couple of days reading documentation.

It’s hard for me to read docs without having some understanding of what I’m reading. So, the last week or so that I’ve spent getting nothing being rendered has been useful into building up some basic knowledge to make the docs somewhat comprehensible.


Structure Of Laravel App

I think understanding the basic structure of Laravel has helped. Installing a barebones Laravel app is easy, understanding how it works is nuts. There are so many parts, my head still hurts.

Anyway, this is my simplistic understanding of the Laravel structure after the app is created.

app.js located at resources/js/app.js is very important.

So is resources/views/app.blade.php and package.json. Package.json is located at the root of the entire app - not in the app folder.

The place to begin is with package.json because you can see what dependencies have been loaded/installed.

{
    "private": true,
    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },
    "devDependencies": {
        "@headlessui/react": "^1.4.2",
        "@inertiajs/inertia": "^0.11.0",
        "@inertiajs/inertia-react": "^0.8.0",
        "@inertiajs/progress": "^0.2.6",
        "@tailwindcss/forms": "^0.5.2",
        "@vitejs/plugin-react": "^2.0.0",
        "autoprefixer": "^10.4.2",
        "axios": "^0.27",
        "laravel-vite-plugin": "^0.5.0",
        "lodash": "^4.17.19",
        "postcss": "^8.4.6",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "tailwindcss": "^3.1.0",
        "vite": "^3.0.0"
    }
}

devDependicies are the packages mainly installed automatically by Laravel.

  1. Inertia which works as the bridge between VUE and Laravel.
  2. Tailwindscss which is another framework css.
  3. Vitejs which is the bundler that Laravel now uses.
  4. autoprefixer looks cool as its a PostCSS plugin which parses CSS and adds vendor prefixes. Lodash is a javascript utility.
  5. Now, I believe I installed material-design-icons with npm after I installed vueistic (which will be discussed later).
  6. postcss seems to work with autoprefixer.

Dependencies are all packages I installed.

Vuestic-UI

Let’s focus on vuestic-ui which a user interface Vue3 framework. I decided to use vueistic because it seems similar to bootstrap and I certainly don’t want to make components from scratch. Nor do I want a hodgepodge of components from different developers.

Once vueistic is installed, its components can be found in Laravel at node_modules/vuestic-ui/dist/esm/src/components. These components then can be used “as-is” or modified and combined into other components.

The instructions to install vuestic.ui are here.

    npm install vuestic-ui

After running this command, vuestic is added as a dependency to ‘package.json’. Like above.

The second instruction is to include Source Sans Pro and Material Icons fonts.

This can done by importing into the <head> element.

So, I went to app.blades.php which is found at resources/views and added in the TWO ASTERISKED Styles Sheets.

<!DOCTYPE html>
<html lang=". . . ">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title inertia> . . . </title>
        
	** <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,400;1,700&display=swap" rel="stylesheet">
        ** <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

        <!-- Scripts -->
        @routes
        @vite('resources/js/app.js')
        @inertiaHead
    </head>

    <body class="font-sans antialiased">

        @inertia

    </body>
</html>

Vueistic also indicates that they can be included in css with the following code. I wonder if it would then work by including these lines in vite.config.js. I’ll have to try it on another day.

@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,400;1,700&display=swap');
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');

Examining app.blades.php, Laravel’s structure is beginning to make more sense.

Under scripts, I can see that routes are called.

There’s also something called @inertiaHead. This is a little confusing. There’s a story from Laravel about inertia-head.. There’s also a story about <head> on inertia. I’m going to have to figure this another day.

Anyway, in app.blades.php I see that app.js is referred to with vite (that make sense because vite is the bundler).

And if we go to app.js in ‘resources/js’, you can see that’s where I added the THREE LINES OF ASTERISKED CODE (**) related to Vuestic.

Two import lines:

import { createVuestic } from 'vuestic-ui'**
import 'vuestic-ui/css'**

And one line that is added to the return

.use(createVuestic()
import './bootstrap';
import '../css/app.css';

**import { createVuestic } from 'vuestic-ui'**
**import 'vuestic-ui/css'**

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';

const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            **.use(createVuestic())**
            .mount(el);
    },
});

InertiaProgress.init({ color: '#4B5563' });

So, now that app.js is where I think it should be, Vueistic components can be added to html code in vue just like bootstrap 5.

I can now type in the name of vueistic components and they will appear.

<va-switch></va-switch>

Vueistic default components rendered


But default components usually need to be modified or combined.

To make it easier to reuse these modified components, one can just make another component from the basic components.

How To Make A Vue Component For Laravel

In resources/js/Components make a file: component_name.vue

The structure within the file is:

<script setup>

Import if needed;

Function if needed;

</script>

<template>

Html code goes here

</template>

[optional css coding]
<style scoped>

.class {
Attribute: ;
}

#id {
Attribute: ;
}

</style>

Once the component is made, just go to the vue file in js/pages and insert the component where it should go.

Here’s a simple example of a component that only has taken me about a week to figure out. The navbar color is #FFFF00 (yellow). And the id #greenman changes the text color to green. Also, note that the tag is <style scoped>. By using scoped, the css is limited to just this file.

<script setup>

</script>

<template>
  <va-navbar color="#FFFF00" shape class="mb-2">
    <template #left>
      <va-navbar-item id="greenman">Green Color</va-navbar-item>
    </template>
    <template #center>
      <va-navbar-item>Primary</va-navbar-item>
    </template>
    <template #right>
      <va-navbar-item>Right</va-navbar-item>
    </template>
  </va-navbar>
</template>

<style scoped>
#greenman {
    Color:green;
}
</style>

I named this file NewNavBar.vue and placed it in resources/js/Components.

Now, if I need to use it in some file in resources/js/Pages, like HappyPage.vue, I can just type in and it should be automatically imported with a yellow Navbar and green text. Fingers crossed.

Yellow Navbar With Green Text

Eureka!!! It works!!!

Okay, now that I got Vue all figured out (yeah, right), it’s time to move on.

TO BE CONTINUED

Visit Emlekezik.com