So, I'm no expert yet, but I'd start from designing my database and from the smallest items Ingredients. To use another example, Library management software would have books.
In our example we start from model even without using a technique called TDD:
1. php make:model ingredient -m
This model we have to edit to have all we need. As we need a group of all ingredients, let's call this group 'list'. So, the next model we need is:
2. php make:model ingr_list -m
Now we can have not only ingredients but as well, we will be able to use many to one polymorphic relations to batch it later. Again we have to edit our model and we have to add one more thing to our migration of 'ingr_lists):
3. $table->unsignedBigInteger('ingredient_id');
And the rest of the model:
//Belongs to many to many group/model: ingr_lists.
public function roles()
{
return $this->belongsToMany('App\ingr_list');
}
Why? If we won't add this one in our model, we won't be able to connect it with our ingredients.
Now both models should be done, edited and migrated. It's time to migrate, so:
4. php artisan migrate
After migration we can create controllers:
5. php artisan make:controller NAME_HERE --resource
My controller is here, but it's not the best of all ;) if you can advise anything, for sure will consider it:
<?php
namespace App\Http\Controllers;
use App\ingredient;
use Illuminate\Http\Request;
class ingredientsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$ingredients = ingredient::orderBy('id')->paginate(20);
return view('ingredients.index')->with('ingredients', $ingredients);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('ingredients.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request,[
'name' => 'required',
]);
//Create new Ingredient
$ingredients = new ingredient;
$ingredients->name = $request->input('name');
$ingredients->unit = $request->input('unit');
$ingredients->qty = $request->input('qty');
//$ingredients->user_id = auth()->user()->id;
$ingredients->save();
return redirect('/ingredients')->with('success', 'Dodano do Bazy');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$ingredients = ingredient::find($id);
return view('ingredients.show')->with('ingredient', $ingredients);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$ingredients = ingredient::find($id);
return view('ingredients.edit')->with('ingredient', $ingredients);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
// public function update(Request $request, $id)
public function update(Request $request, ingredient $ingredients, $id)
{
$this->validate($request,[
'name' => 'required'
]);
//Update an Ingredient
$ingredients = Ingredient::find ($id);
$ingredients->name = $request->input('name');
$ingredients->unit = $request->input('unit');
$ingredients->qty = $request->input('qty');
$ingredients->save();
$ingredients->update($request->all());
return redirect('/ingredients')->with('success', "Skladnik zostal zaktualizowany");
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$Ingredients = Ingredient::find($id);
$Ingredients->delete();
return redirect('/Ingredients')->with('success', 'Ingredient Deleted');
}
}
----------------------------------------------------------------------------------------
This is the first controller and we can use this data to create another one.
6. Now it's time to make a route for ourselves. Routes are described on this blog, so I won't mention them.
7. We should add to our ingredient model as well this line in our function:
protected $fillable = ['unit', 'foo', 'bar'];
Otherwise, during EDIT of our ingredients, we won't be able to submit successfully an update. Instead, we might see an error about a token. You can google more by pasting the content of this line in google. Unit word is one of the names in our table in our database.
8. Now it's time for View pages. I follow always the same structure to not complicate myself other/next projects later. And remember what I've done in the past.
In the resources folder, I click on a View and create a new folder with the same name what my controller is so 'ingredients'. Inside this folder, I always create the same .blade.php files where I can use a blade of course. They are as follows: index, show, edit, create.
Here how my not perfect files are looking. To make a quick and simple design for pages I've used standard bootstrap.
Here is an index.blade.php file:
<div class="container">
@extends('layouts.app')
@section('content')
<h1>Lista wszystkich skladnikow</h1>
<hr>
@if(count($ingredients) > 0)
@foreach($ingredients as $ingredient)
<table class="table table-striped">
<div class="row">
<div class="col-6">
<h3><a href="/ingredients/{{$ingredient->id}}">{{$ingredient->name}}</a></h3>
</div>
<div class="col-3">
<h4>W ilosci: {{$ingredient->qty}} {{$ingredient->unit}}</h4>
</div>
<div class="col-3">
<a href="/ingredients/{{$ingredient->id}}/edit" class="btn btn-info float-right">Edytuj</a>
</div>
</div>
</table><hr>
@endforeach
<br>
{{$ingredients->links()}}
@else
<p>Brak skladnikow w bazie<p>
@endif
<a href="/ingredients/create" class="btn btn-success">Dodaj nowy skladnik</a>
</div>
@endsection
Now edit.blade.php file:
<div class="container">
@extends('layouts.app')
@section('content')
<div class="row">
<h1>Edytuj Skladnik</h1>
</div>
<div class="form-group">
<form action="/ingredients/{{ $ingredient->id }}" method="POST">
{{ csrf_field() }}
@method('PATCH')
<input type="text" name="name" placeholder="Nazwa" class="form-control" value="{{$ingredient->name}}"><br><br>
<input type="text" name="qty" placeholder="Ilosc" class="form-control" value="{{$ingredient->qty}}"><br><br>
<input type="text" name="unit" placeholder="Jednostka Miary" class="form-control" value="{{$ingredient->unit}}"><br><br>
<button type="submit" class="btn btn-primary"> Zapisz</button>
</form>
</div>
</div>
<a href="/ingredients" class="btn btn-success">Przejdz do listy</a>
@endsection
Now show.blade.php file:
<div class="container">
@extends('layouts.app')
@section('content')
<div class="row">
<div class="col">
<a href="/ingredients" class="btn btn-success">Wroc do listy</a>
</div>
</div>
<br>
<div class="row">
<div class="col">
<h1>{{$ingredient->name}}</h1>
</div>
</div>
<br>
<div class="row">
<div class="col-3">
<h4>Jednostka miary:  {{$ingredient->unit}}</h4>
</div>
<br>
<div class="col-3">
<h4>Ilosc:  {{$ingredient->qty}}</h4><br>
</div>
</div>
<div class="row">
<div class="col-6">
<a href="/ingredients/{{$ingredient->id}}/edit" class="btn btn-info">Edytuj</a>
</div>
<div class="col-6">
<a href="/ingredients/create" class="btn btn-success">Dodaj nowy skladnik</a>
</div>
</div>
</div>
<br><br>
</div>
@endsection
Last but not least is create.blade.php file:
<div class="container">
@extends('layouts.app')
@section('content')
<div class="row">
<div class="col">
<a href="/ingredients" class="btn btn-success">
Przejdz do listy</a>
</div>
</div>
<hr>
<div class="row">
<div class="col">
<h1>Dodaj Nowy Skladnik</h1>
</div>
</div>
<br>
<div class="col">
<form action="/ingredients" method="POST">
{{ csrf_field() }}
<input type="text" name="name" placeholder="Nazwa"
class="form-control"><br><br>
<input type="text" name="qty" placeholder="Ilosc"
class="form-control"><br><br>
<input type="text" name="unit" placeholder="Jednostka Miary"
class="form-control"><br><br>
<button type="submit" class="btn btn-primary"> Zapisz
</button>
</form>
</div>
</div>
@endsection
These are REALLY basic files and there's no even @error handling which is super easy to add. Planty of things we can add to it, but I want to focus on basics. There are no even notifications to show message after submission, edit, or delete. I'll try to find more time to show them on a separate page just to understand them better.
That's it, I can only show you how they look like from the page side. Sorry for this outgoing code, but Blogger is not the best when it comes to editing pages and posts. I've tried to fit everything at create.blade.php, but it started to be messier and hence for that, I've decided to neglect the rest of them.
Here is a list view, so index.blade.php:
Name of ingredient on the left, at the very top we've got our menu from Auth installed. Just below there's H1 Name of the category. In the center word 'W ilosci:' is written just in HTML, but data on the right is triggered from our DB in QTY and unit. At the very bottom, the green button means Add a new ingredient.
Next is show.blade.php file, all you have to do is click on the name and will re-direct you there:
'Wroc do listy' green button is just Back to list. Below is the name of the category and below is written in HTML 'Jednostka miary' which is measuring unit: and the unit is taken from our DB. 'Ilosc' which is QTY is written in HTML, but the value is taken from DB. 'Edytuj' blue button is Edit and again the last button is the same what on Index to Add a new Ingredient.
I'll show you only edit.blade.php, no point to show just create. I've used placeholder in the form to not put a label to save time. So, you can see only values triggered in each field from the DB. 'Zapisz' blue button means Save, and green 'Przejdz do Listy' button means Go back to List.
As you see this is a simple CRUD/BREAD control over your data. But, it's a good start for something great!
It should show you that we don't have to use admin panels all the time ;). Try to create your stuff by using this tutorial and check how long it will take you :). Just keep in mind to use Laravel 6 to have full compatibility.
The next step will be joining this model with a list of all ingredients and create a meal by adding to our list, description, and photo with a name. We will have to use finally polymorphic relationships to achieve it.
Later, we can use Spatie permissions to limit access to users, or even Multi-tenancy to create something like SaaS so that each user can have their own account/profile and have their own menu planner just for themselves. We could do everything by hand, but if I would not use bootstrap or Laravel's Auth, then I would have to spend far more time then I did.
If/when a customer wants and is able to pay more, of course, I'd be happy to use only CSS, use maybe customer bootstrap only for grid setup, or even not. I can even create every module by hand, but, most of the time I'd only waste the time if/when it's not important to do it. Custome design is always dearer and sometimes is really worth it for the customer.



No comments:
Post a Comment