Skip to content

Commit 15296d2

Browse files
authored
Merge pull request #15714 from akemidx/saving_custom_report_template
Saved Custom Report Templates
2 parents 1434522 + 92f33c0 commit 15296d2

28 files changed

+1593
-212
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace App\Http\Controllers;
4+
5+
use App\Models\CustomField;
6+
use App\Models\ReportTemplate;
7+
use Illuminate\Http\RedirectResponse;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Arr;
10+
11+
class ReportTemplatesController extends Controller
12+
{
13+
public function store(Request $request): RedirectResponse
14+
{
15+
$this->authorize('reports.view');
16+
17+
// Ignore "options" rules since data does not come in under that key...
18+
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
19+
20+
$report = $request->user()->reportTemplates()->create([
21+
'name' => $validated['name'],
22+
'options' => $request->except(['_token', 'name']),
23+
]);
24+
25+
session()->flash('success', trans('admin/reports/message.create.success'));
26+
27+
return redirect()->route('report-templates.show', $report->id);
28+
}
29+
30+
public function show(ReportTemplate $reportTemplate)
31+
{
32+
$this->authorize('reports.view');
33+
34+
$customfields = CustomField::get();
35+
$report_templates = ReportTemplate::orderBy('name')->get();
36+
37+
return view('reports/custom', [
38+
'customfields' => $customfields,
39+
'report_templates' => $report_templates,
40+
'template' => $reportTemplate,
41+
]);
42+
}
43+
44+
public function edit(ReportTemplate $reportTemplate)
45+
{
46+
$this->authorize('reports.view');
47+
48+
return view('reports/custom', [
49+
'customfields' => CustomField::get(),
50+
'template' => $reportTemplate,
51+
]);
52+
}
53+
54+
public function update(Request $request, ReportTemplate $reportTemplate): RedirectResponse
55+
{
56+
$this->authorize('reports.view');
57+
58+
// Ignore "options" rules since data does not come in under that key...
59+
$validated = $request->validate(Arr::except((new ReportTemplate)->getRules(), 'options'));
60+
61+
$reportTemplate->update([
62+
'name' => $validated['name'],
63+
'options' => $request->except(['_token', 'name']),
64+
]);
65+
66+
session()->flash('success', trans('admin/reports/message.update.success'));
67+
68+
return redirect()->route('report-templates.show', $reportTemplate->id);
69+
}
70+
71+
public function destroy(ReportTemplate $reportTemplate): RedirectResponse
72+
{
73+
$this->authorize('reports.view');
74+
75+
$reportTemplate->delete();
76+
77+
return redirect()->route('reports/custom')
78+
->with('success', trans('admin/reports/message.delete.success'));
79+
}
80+
}

app/Http/Controllers/ReportsController.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use App\Models\CustomField;
1515
use App\Models\Depreciation;
1616
use App\Models\License;
17+
use App\Models\ReportTemplate;
1718
use App\Models\Setting;
1819
use App\Notifications\CheckoutAssetNotification;
1920
use Carbon\Carbon;
@@ -394,12 +395,27 @@ public function exportLicenseReport() : Response
394395
* @see ReportsController::postCustomReport() method that generates the CSV
395396
* @since [v1.0]
396397
*/
397-
public function getCustomReport() : View
398+
public function getCustomReport(Request $request) : View
398399
{
399400
$this->authorize('reports.view');
400401
$customfields = CustomField::get();
402+
$report_templates = ReportTemplate::orderBy('name')->get();
401403

402-
return view('reports/custom')->with('customfields', $customfields);
404+
// The view needs a template to render correctly, even if it is empty...
405+
$template = new ReportTemplate;
406+
407+
// Set the report's input values in the cases we were redirected back
408+
// with validation errors so the report is populated as expected.
409+
if ($request->old()) {
410+
$template->name = $request->old('name');
411+
$template->options = $request->old();
412+
}
413+
414+
return view('reports/custom', [
415+
'customfields' => $customfields,
416+
'report_templates' => $report_templates,
417+
'template' => $template,
418+
]);
403419
}
404420

405421
/**

app/Models/ReportTemplate.php

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Factories\HasFactory;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
9+
use Illuminate\Database\Eloquent\SoftDeletes;
10+
use Watson\Validating\ValidatingTrait;
11+
12+
class ReportTemplate extends Model
13+
{
14+
use HasFactory;
15+
use SoftDeletes;
16+
use ValidatingTrait;
17+
18+
protected $casts = [
19+
'options' => 'array',
20+
];
21+
22+
protected $fillable = [
23+
'created_by',
24+
'name',
25+
'options',
26+
];
27+
28+
protected $rules = [
29+
'name' => [
30+
'required',
31+
'string',
32+
],
33+
'options' => [
34+
'required',
35+
'array',
36+
],
37+
];
38+
39+
protected static function booted()
40+
{
41+
// Scope to current user
42+
static::addGlobalScope('current_user', function (Builder $builder) {
43+
if (auth()->check()) {
44+
$builder->where('created_by', auth()->id());
45+
}
46+
});
47+
48+
static::created(function (ReportTemplate $reportTemplate) {
49+
$logAction = new Actionlog([
50+
'item_type' => ReportTemplate::class,
51+
'item_id' => $reportTemplate->id,
52+
'created_by' => auth()->id(),
53+
]);
54+
55+
$logAction->logaction('create');
56+
});
57+
58+
static::updated(function (ReportTemplate $reportTemplate) {
59+
$changed = [];
60+
61+
foreach ($reportTemplate->getDirty() as $key => $value) {
62+
$changed[$key] = [
63+
'old' => $reportTemplate->getOriginal($key),
64+
'new' => $reportTemplate->getAttribute($key),
65+
];
66+
}
67+
68+
$logAction = new Actionlog();
69+
$logAction->item_type = ReportTemplate::class;
70+
$logAction->item_id = $reportTemplate->id;
71+
$logAction->created_by = auth()->id();
72+
$logAction->log_meta = json_encode($changed);
73+
$logAction->logaction('update');
74+
});
75+
76+
static::deleted(function (ReportTemplate $reportTemplate) {
77+
$logAction = new Actionlog([
78+
'item_type' => ReportTemplate::class,
79+
'item_id' => $reportTemplate->id,
80+
'created_by' => auth()->id(),
81+
]);
82+
83+
$logAction->logaction('delete');
84+
});
85+
}
86+
87+
/**
88+
* Establishes the report template -> creator relationship.
89+
*
90+
*/
91+
public function creator(): BelongsTo
92+
{
93+
return $this->belongsTo(User::class, 'created_by');
94+
}
95+
96+
/**
97+
* Get the value of a checkbox field for the given field name.
98+
*
99+
* @param string $fieldName
100+
* @param string $fallbackValue The value to return if the report template is not saved yet.
101+
*
102+
*/
103+
public function checkmarkValue(string $fieldName, string $fallbackValue = '1'): string
104+
{
105+
// Assuming we're using the null object pattern, and an empty model
106+
// was passed to the view when showing the default report page,
107+
// return the fallback value so that checkboxes are checked by default.
108+
if (is_null($this->id)) {
109+
return $fallbackValue;
110+
}
111+
112+
// If the model does exist then return the value of the field
113+
// or return 0 so the checkbox is unchecked.
114+
// Falling back to 0 here is because checkboxes are not sent
115+
// in the request when unchecked so they are not
116+
// actually saved in the model's options.
117+
return $this->options[$fieldName] ?? '0';
118+
}
119+
120+
/**
121+
* Get the value of a radio field for the given field name.
122+
*
123+
* @param string $fieldName
124+
* @param string $value The value to check against.
125+
* @param bool $isDefault Whether the radio input being checked is the default.
126+
*
127+
*/
128+
public function radioValue(string $fieldName, string $value, bool $isDefault = false): bool
129+
{
130+
$fieldExists = array_has($this->options, $fieldName);
131+
132+
// If the field doesn't exist but the radio input
133+
// being checked is the default then return true.
134+
if (!$fieldExists && $isDefault) {
135+
return true;
136+
}
137+
138+
// If the field exists and matches what we're checking then return true.
139+
if ($fieldExists && $this->options[$fieldName] === $value) {
140+
return true;
141+
}
142+
143+
// Otherwise return false.
144+
return false;
145+
}
146+
147+
/**
148+
* Get the value of a select field for the given field name.
149+
*
150+
* @param string $fieldName
151+
* @param string|null $model The Eloquent model to check against.
152+
*
153+
* @return mixed|null
154+
*
155+
*/
156+
public function selectValue(string $fieldName, string $model = null)
157+
{
158+
// If the field does not exist then return null.
159+
if (!isset($this->options[$fieldName])) {
160+
return null;
161+
}
162+
163+
$value = $this->options[$fieldName];
164+
165+
// If the value was stored as an array, most likely
166+
// due to a previously being a multi-select,
167+
// then return the first value.
168+
if (is_array($value)) {
169+
$value = $value[0];
170+
}
171+
172+
// If a model is provided then we should ensure we only return
173+
// the value if the model still exists.
174+
// Note: It is possible $value is an id that no longer exists and this will return null.
175+
if ($model) {
176+
$foundModel = $model::find($value);
177+
178+
return $foundModel ? $foundModel->id : null;
179+
}
180+
181+
return $value;
182+
}
183+
184+
/**
185+
* Get the values of a multi-select field for the given field name.
186+
*
187+
* @param string $fieldName
188+
* @param string|null $model The Eloquent model to check against.
189+
*
190+
* @return iterable
191+
*
192+
*/
193+
public function selectValues(string $fieldName, string $model = null): iterable
194+
{
195+
// If the field does not exist then return an empty array.
196+
if (!isset($this->options[$fieldName])) {
197+
return [];
198+
}
199+
200+
// If a model is provided then we should ensure we only return
201+
// the ids of models that exist and are not deleted.
202+
if ($model) {
203+
return $model::findMany($this->options[$fieldName])->pluck('id');
204+
}
205+
206+
// Wrap the value in an array if needed. This is to ensure
207+
// values previously stored as a single value,
208+
// most likely from a single select, are returned as an array.
209+
if (!is_array($this->options[$fieldName])) {
210+
return [$this->options[$fieldName]];
211+
}
212+
213+
return $this->options[$fieldName];
214+
}
215+
216+
/**
217+
* Get the value of a text field for the given field name.
218+
*
219+
* @param string $fieldName
220+
* @param string|null $fallbackValue
221+
*
222+
* @return string
223+
*/
224+
public function textValue(string $fieldName, string|null $fallbackValue = ''): string
225+
{
226+
// Assuming we're using the null object pattern,
227+
// return the default value if the object is not saved yet.
228+
if (is_null($this->id)) {
229+
return (string) $fallbackValue;
230+
}
231+
232+
// Return the field's value if it exists
233+
// and return the default value if not.
234+
return $this->options[$fieldName] ?? '';
235+
}
236+
237+
public function getDisplayNameAttribute()
238+
{
239+
return $this->name;
240+
}
241+
}

app/Models/User.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Illuminate\Contracts\Translation\HasLocalePreference;
1414
use Illuminate\Database\Eloquent\Builder;
1515
use Illuminate\Database\Eloquent\Factories\HasFactory;
16+
use Illuminate\Database\Eloquent\Relations\HasMany;
1617
use Illuminate\Database\Eloquent\SoftDeletes;
1718
use Illuminate\Foundation\Auth\Access\Authorizable;
1819
use Illuminate\Notifications\Notifiable;
@@ -361,6 +362,15 @@ public function licenses()
361362
return $this->belongsToMany(\App\Models\License::class, 'license_seats', 'assigned_to', 'license_id')->withPivot('id', 'created_at', 'updated_at');
362363
}
363364

365+
/**
366+
* Establishes the user -> reportTemplates relationship
367+
*
368+
*/
369+
public function reportTemplates(): HasMany
370+
{
371+
return $this->hasMany(ReportTemplate::class, 'created_by');
372+
}
373+
364374
/**
365375
* Establishes a count of all items assigned
366376
*

0 commit comments

Comments
 (0)