Taskx mittels Laraknife
Links
Zielsetzung
Es soll eine minimale Web-Applikation mit den Werkzeugen Laravel und Laraknife erstellt werden: Eine Verwaltung von Notizen.
Name
Eine Verballhornung des Wortes "Tasks": Die schwäbische Aussprache spricht ein x am Ende.
Eigenschaften
- Benutzerverwaltung: nur angemeldete Benutzer können Applikation nutzen
- Rechteverwaltung mittels Rollen
- Verwaltung von Notizen: Titel, Text, Kategorie, Text, Status: offen, erledigt
Installation
- als normaler Benutzer (nicht root):
sudo apt install php-laravel-framework npm php-intl
PROJ=taskx
PASSW=topsecret
BASE=/home/ws/php/$PROJ
cd $(dirname $BASE)
composer create-project laravel/laravel $PROJ
cd $BASE
composer require laravel/ui
composer require spatie/laravel-permission
php artisan ui bootstrap --auth
dbtool create-db-and-user lrv$PROJ $PROJ "$PASSW"
sed -i -e "s/DB_DATABASE=.*/DB_DATABASE=lrv$PROJ/" \
-e "s/DB_USERNAME=.*/DB_USERNAME=$PROJ/" \
-e "s/DB_PASSWORD=.*/DB_PASSWORD=$PASSW/" .env
M_HOST=mail.gmx.net
M_PORT=587
M_USER=example@gmx.de
M_PW=Top.Secret42
sed -i \
-e "s/APP_NAME=.*/APP_NAME=$PROJ"/ \
-e "s/MAIL_MAILER=.*/MAIL_MAILER=smtp/" \
-e "s/MAIL_HOST=.*/MAIL_HOST=$M_HOST/" \
-e "s/MAIL_PORT=.*/MAIL_PORT=$M_PORT/" \
-e "s/MAIL_USERNAME=.*/MAIL_USERNAME=$M_USER/" \
-e "s/MAIL_PASSWORD=.*/MAIL_PASSWORD=$M_PW/" \
-e "s/MAIL_ENCRYPTION=.*/MAIL_ENCRYPTION=STARTTLS/" \
-e "s/MAIL_FROM_ADDRESS=.*/MAIL_FROM_ADDRESS=\"$M_USER\"/" .env
sudo sed -i -e "3 i 127.0.0.1 $PROJ.test" /etc/hosts
grep "$PROJ.test" /etc/hosts
php artisan migrate
npm install
npm run dev
# Abbruch mit Strg-C
Einrichten LaraKnife
composer config repositories.laraknife vcs https://github.com/hamatoma/laraknife
# Branch main:
composer require hamatoma/laraknife:dev-main
composer update
vendor/hamatoma/laraknife/scripts/laraknife-tool.sh build-links
Starten Webserver
php artisan serve
- Im Browser: http://localhost:8000
Modul Notes erstellen
Tabellenbeschreibung erstellen
- Konvention: Tabelle wird kleingeschrieben, im Plural
TABLE=notes
php artisan make:migration create_${TABLE}_table
- Es wird die Datei database/migrations/2023_12_29_180821_create_notes_table.php erzeugt
- Diese Datei anpassen:
Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('title'); $table->text('body'); // foreign key of sproperties. $table->integer('category_id'); // foreign key of sproperties. $table->integer('status_id'); // foreign key of sproperties. $table->integer('group_id'); $table->foreignId('user_id')->references('id')->on('users')->nullable(); });
Modul erzeugen
php artisan migrate
./Lara create:module database/migrations/2023_12_29_180821_create_notes_table.php
Roles und SProperties füllen
sudo mysql lrv$PROJ <<'EOS'
insert into roles (name, priority, created_at, updated_at) values
('Administrator', 10, '2023.12.28', '2023-12-28'),
('Manager', 20, '2023.12.28', '2023-12-28'),
('User', 30, '2023.12.28', '2023-12-28'),
('Guest', 90, '2023.12.28', '2023-12-28');
insert into sproperties (id, scope, name, `order`, shortname, created_at) values
(1001, 'status', 'active', 10, 'A', '2023-12-28'),
(1002, 'status', 'inactive', 20, 'I', '2023-12-28'),
(2001, 'category', '<none>', 10, '-', '2023-12-28'),
(2002, 'category', 'private', 20, 'P', '2023-12-28'),
(2003, 'category', 'work', 30, 'W', '2023-12-28'),
(2501, 'group', '<none>', 10, '-', '2023-12-28'),
(2502, 'group', 'private', 20, 'P', '2023-12-28'),
(2503, 'group', 'work', 20, 'W', '2023-12-28'),
(2601, 'notestatus', 'open', 10, 'O', '2023-12-28'),
(2602, 'notestatus', 'closed', 20, 'C', '2023-12-28');
EOS
Layout erstellen
- Datei resources/views/layouts/taskx.blade.php erstellen:
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<!-- link rel="dns-prefetch" href="//fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet" -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
<link href="/css/laraknife.css" rel="stylesheet">
<link href="/css/langutor.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/laraknife.js"></script>
</head>
<body>
<header>
<nav class="navbar navbar-expand-md bg-primary ">
<a class="navbar-brand" href="/home">Booking</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTop">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Start</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user-edit">{{ __('Settings') }}</a>
</li>
<li>
<a class="nav-link" href="/public/doc/Impressum.pdf" target="_blank">{{ __('Imprint') }}</a>
</li>
<li>
<a class="nav-link" href="/public/doc/Datenschutz.pdf" target="_blank">{{ __('Privacy') }}</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0" action="/logout" method="post">
<button class="btn btn-outline-success my-2 my-sm-0 logout" name="btnLogout"
type="submit"> {{ __('Logout') }}</button>
</form>
</div>
</nav>
</header>
@yield('content')
</body>
</html>
cd resources/views/layouts
ln -s taskx.blade.php backend.blade.php
cd ../..
Homepage einrichten
- Datei resources/views/home.blade.php
@extends('layouts.taskx')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
<ul>
<li><a href="/sproperty-index">SProperties</a></li>
<li><a href="/user-index">Benutzer</a></li>
<li><a href="/role-index">Rollen</a></li>
<li><a href="/notes-index">Notizen</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
@endsection
Routing einrichten
- In Datei routes/web.php einfügen:
SPropertyController::routes();
RoleController::routes();
UserController::routes();
NoteController::routes();
Starten, Registrieren, Login
- Den Webserver starten:
php artisan serve
- Im Brower aufrufen: http://localhost:8000
- Oben rechts: Register
- Felder ausfüllen
- Oben rechts: Login
Controller und Views anpassen
- Datei app/Http/Controllers/NoteController.php
public function index()
{
if (array_key_exists('btnSubmit', $_POST) && $_POST['btnSubmit'] === 'btnNew') {
return redirect('/note-create');
} else {
$sql = 'SELECT title, body, user_id, cast(body AS VARCHAR(60)) as body2 FROM notes';
$userId = auth()->id();
$parameters = [':user_id' => $userId];
$conditions = ['user_id = :user_id'];
if (count($_POST) == 0) {
$fields = [
'text' => '',
'_sortParams' => 'id:desc'
];
} else {
$fields = $_POST;
ViewHelper::addConditionPattern($conditions, $parameters, 'title,body', 'text');
}
$sql = DbHelper::addConditions($sql, $conditions);
$sql = DbHelper::addOrderBy($sql, $fields['_sortParams']);
$records = DB::select($sql, $parameters);
$pagination = new Pagination($sql, $parameters, $fields);
return view('note.index', [
'records' => $records,
'fields' => $fields,
'pagination' => $pagination
]);
}
}
- Datei resources/views/note/index.blade.php
form id="note-index" action="/note-index" method="POST">
@csrf
<x-laraknife.index-panel title="{{ __('Notes') }}">
<x-laraknife.filter-panel legend="{{ $pagination->legendText() }}">
<x-laraknife.text position="alone" name="text" label="Text" value="{{$fields['text']}}" width2="4" />
</x-laraknife.filter-panel>
<x-laraknife.index-button-panel buttonType="new"/>
<x-laraknife.sortable-table-panel :fields="$fields" :pagination="$pagination">
<thead>
<tr>
<th></th>
<th sortId="title">{{__('Title')}}</th>
<th sortId="body">{{__('Body')}}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach ($records as $note)
<tr>
<td><a href="/note-edit/{{$note->id}}">{{ __('Change')}}</a></td>
<td>{{$note->title}}</td>
<td>{{$note->body2}}</td>
<!--td>{{$note->category}}</td>
<td>{{$note->group}}</td>
<td>{{$note->status}}</td-->
<td><a href="/note-delete/{{$note->id}}">{{ __('Delete')}}</a></td>
</tr>
@endforeach
</tbody>
</x-laraknife.sortable-table-panel>
</x-laraknife.index-panel>
</form>