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, Status: offen, erledigt
Installation
- als normaler Benutzer (nicht root):
SCRIPT=/tmp/CreateLaraProj.sh
wget "https://public.hamatoma.de/public/CreateLaraProj.sh" -O $SCRIPT
chmod +x $SCRIPT
PROJ=taskx
PASSW=topsecret
BASE=/home/ws/php
$SCRIPT $PROJ "$PASSW" "$BASE"
- Am Ende wird dazu aufgefordert, das Script /tmp/IncludeLara.sh zu starten:
/tmp/IncludeLara.sh git $BASE/$PROJ
Mehrsprachigkeit (I18N)
larascripts/laraknife-tool.sh init-i18n
./Join
- Die neuen Übersetzungen müssen in resources/lang/sources/taskx.de.json eingetragen werden.
Roles und SProperties füllen
larascripts/laraknife-tool.sh fill-db
Laraknife-Module anpassen
larascripts/laraknife-tool.sh adapt-modules
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_scope'); // foreign key of sproperties. $table->integer('status_scope'); $table->foreignId('user_id')->references('id')->on('users')->nullable(); });
Modul erzeugen
php artisan migrate
./Lara create:module database/migrations/*_create_notes_table.php
Layout erstellen
- Datei resources/views/layouts/taskx.blade.php erstellen:
larascripts/laraknife-tool.sh create-layout
Routing einrichten
- In Datei routes/web.php einfügen:
SPropertyController::routes();
RoleController::routes();
UserController::routes();
NoteController::routes();
- Es sind 4 Namen unterstrichen: mit Strg-Alt-I Klassen einbinden.
Starten, Registrieren, Login
- Den Webserver starten:
./Build
php artisan serve
- Im Brower aufrufen: http://localhost:8000
- Oben rechts: Register
- Felder ausfüllen
- Oben rechts: Login
Controller und Views anpassen
Übersicht (index)
- 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 id, 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="id">{{__('Id')}}</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->id}}</td>
<td>{{$note->title}}</td>
<td>{{$note->body2}}</td>
<td><a href="/note-delete/{{$note->id}}">{{ __('Delete')}}</a></td>
</tr>
@endforeach
</tbody>
</x-laraknife.sortable-table-panel>
</x-laraknife.index-panel>
Anlegen (create)
- Datei app/Http/Controllers/NoteController.php
- Die drei Methoden ersetzen:
public function create(Request $request)
{
if (array_key_exists('btnSubmit', $_POST) && $_POST['btnSubmit'] === 'btnCancel') {
$rc = redirect('/note-index');
} else {
$rc = null;
$error = null;
if (count($_POST) > 0) {
$fields = $_POST;
try {
$incomingFields = $request->validate($this->rules(true));
$rc = $this->store($request);
} catch (\Exception $e) {
$error = $e->getMessage();
}
} else {
$fields = [
'title' => '',
'body' => '',
'category_scope' => '2001',
'status_scope' => '2601',
'group_scope' => '2501',
];
}
if ($rc == null) {
$optionsCategory = SProperty::optionsByScope('category', $fields['category_scope'], '-');
$optionsStatus = SProperty::optionsByScope('notestatus', $fields['status_scope'], '-');
$optionsGroup = SProperty::optionsByScope('group', $fields['group_scope'], '-');
$rc = view('note.create', ['fields' => $fields,
'optionsCategory' => $optionsCategory,
'optionsStatus' => $optionsStatus,
'optionsGroup' => $optionsGroup,
'error' => $error]);
}
}
return $rc;
}
private function rules(bool $isCreation=false): array
{
$rc = [
'title' => 'required',
'body' => 'required',
'category_scope' => 'required',
'status_scope' => 'required',
'group_scope' => 'required',
];
return $rc;
}
public function store(Request $request)
{
if ($request->btnSubmit === 'btnStore') {
$incomingFields = $request->validate($this->rules());
$incomingFields['user_id'] = auth()->id();
$incomingFields['body'] = strip_tags($incomingFields['body']);
Note::create($incomingFields);
}
return redirect('/note-index');
}
- Datei resources/views/note/create.blade.php
<form id="note-create" action="/note-create" method="POST">
@csrf
<x-laraknife.create-panel title="{{ __('Creation of a Note') }}" error="{{$error}}">
<x-laraknife.combobox position="first" name="status_scope" label="Status" value="{{$fields['status_scope']}}" :options="$optionsStatus" width2="4" />
<x-laraknife.combobox position="last" name="group_scope" label="Group" value="{{$fields['group_scope']}}" :options="$optionsGroup" width2="4" />
<x-laraknife.combobox position="alone" name="category_scope" label="Category" value="{{$fields['category_scope']}}" :options="$optionsCategory" width2="4" />
<x-laraknife.text position="alone" name="title" label="Title" value="{{$fields['title']}}" />
<x-laraknife.bigtext position="alone" name="body" label="Body" value="{{$fields['body']}}" rows="10" />
</x-laraknife.create-panel>
</form>
- Übersetzungen in resources/lang/sources/taskx.de.json
"Body": "Info", "Category": "Kategorie", "Creation of a Note": "Anlegen einer Notiz", "Group": "Gruppe", "open": "offen", "standard": "Standard", "Status": "Status", "Title": "Titel",
- Aktivieren:
./Join
Ändern (edit)
- Datei app/Http/Controllers/NoteController.php
public function edit(Note $note)
{
if (array_key_exists('btnSubmit', $_POST) && $_POST['btnSubmit'] === 'btnCancel') {
$rc = redirect('/note-index');
} else {
$optionsCategory = SProperty::optionsByScope('category', $note->category_scope, '-');
$optionsStatus = SProperty::optionsByScope('notestatus', $note->status_scope, '-');
$optionsGroup = SProperty::optionsByScope('group', $note->group_scope, '-');
$rc = view('note.edit', [
'note' => $note,
'optionsCategory' => $optionsCategory,
'optionsStatus' => $optionsStatus,
'optionsGroup' => $optionsGroup,
]);
}
return $rc;
}
public function update(Note $note, Request $request)
{
$rc = null;
if ($request->btnSubmit === 'btnStore') {
try {
$incomingFields = $request->validate($this->rules());
$incomingFields['body'] = strip_tags($incomingFields['body']);
$note->update($incomingFields);
$rc = redirect('/note-index');
} catch (\Exception $exc) {
$msg = $exc->getMessage();
$rc = back();
}
}
if ($rc == null) {
$rc = redirect('/note-index');
}
return $rc;
}
- Datei resources/views/note/edit.blade.php
<form id="note-edit" action="/note-update/{{ $note->id }}" method="POST">
@csrf
<x-laraknife.edit-panel title="{{ __('Change of a Note') }}">
<x-laraknife.combobox position="first" name="status_scope" label="Status" value="{{$note->status_scope}}" :options="$optionsStatus" width2="4" />
<x-laraknife.combobox position="last" name="group_scope" label="Group" value="{{$note->group_scope}}" :options="$optionsGroup" width2="4" />
<x-laraknife.combobox position="alone" name="category_scope" label="Category" value="{{$note->category_scope}}" :options="$optionsCategory" width2="4" />
<x-laraknife.text position="alone" name="title" label="Title" value="{{$note->title}}" />
<x-laraknife.bigtext position="alone" name="body" label="Body" value="{{$note->body}}" rows="10" />
</x-laraknife.edit-panel>
</form>
- Übersetzungen in resources/lang/sources/taskx.de.json
"Change of a Note": "Ändern einer Notiz",
- Aktivieren:
./Join
Anzeigen (show/delete)
- Datei app/Http/Controllers/NoteController.php
public function show(Note $note)
{
if (array_key_exists('btnSubmit', $_POST) && $_POST['btnSubmit'] === 'btnCancel') {
$rc = redirect('/note-index');
} else {
$optionsCategory = SProperty::optionsByScope('category', 'category', '');
$optionsGroup = SProperty::optionsByScope('group', 'group', '');
$optionsStatus = SProperty::optionsByScope('notestatus', 'status', '');
$rc = view('note.show', [
'note' => $note,
'optionsCategory' => $optionsCategory,
'optionsGroup' => $optionsGroup,
'optionsStatus' => $optionsStatus,
'mode' => 'delete'
]);
}
return $rc;
}
- Datei resources/views/note/show.blade.php
<form id="note-show" action="/note-show/{{ $note->id }}/{{ $mode }}" method="POST">
@csrf
@if ($mode === 'delete')
@method('DELETE')
@endif
<x-laraknife.show-panel title="{{ __($mode !== 'delete' ? 'A Note' : 'Deletion of a Note') }}"
mode="{{ $mode }}">
<x-laraknife.combobox position="first" name="status_scope" label="Status" value="{{ $note->status_scope }}"
:options="$optionsStatus" width2="4" attribute="readonly" />
<x-laraknife.combobox position="last" name="group_scope" label="Group" value="{{ $note->group_scope }}"
:options="$optionsGroup" width2="4" attribute="readonly" />
<x-laraknife.combobox position="first" name="category_scope" label="Category" value="{{ $note->category_scope }}"
:options="$optionsCategory" width2="4" attribute="readonly" />
<x-laraknife.text position="last" name="id" label="Id" value="{{ $note->id }}" width2="4"
attribute="readonly" />
<x-laraknife.text position="alone" name="title" label="Title" value="{{ $note->title }}"
attribute="readonly" />
<x-laraknife.bigtext position="alone" name="body" label="Body" value="{{ $note->body }}" rows="10"
attribute="readonly" />
</x-laraknife.show-panel>
</form>
- Übersetzungen in resources/lang/sources/taskx.de.json
"A Note" : "Eine Notiz", "Deletion of a Note": "Löschen einer Notiz",
- Aktivieren:
./Join