Commit 7e52c9e8 by Mohammad Izzat Johari

initial commit

parents
docs/node_modules
vendor
composer.lock
# Alurkerja CRUD
{
"name": "alurkerja-laravolt/crud",
"type": "package",
"version": "1.0.2",
"description": "Alurkerja Lowcode",
"keywords": ["laravel", "alurkerja", "lowcode", "crud", "workflow"],
"license": "MIT",
"require": {
"php": "^8.1",
"doctrine/dbal": "^3.6",
"laravolt/camunda": "^2.5",
"laravolt/laravolt": "^5.6",
"ramsey/uuid": "^4.7",
"beyondcode/laravel-websockets": "^1.14"
},
"require-dev": {
"knuckleswtf/scribe": "^4.18",
"phpstan/phpstan": "^1.10"
},
"autoload": {
"psr-4": {
"Laravolt\\Crud\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"Laravolt\\Crud\\CrudServiceProvider"
]
}
}
}
<?php
return [
"ID" => [
"create_button_label" => "Tambah %s",
"detail_button_label" => "Detail %s",
"edit_button_label" => "Edit %s",
"delete_button_label" => "Delete %s",
"start_process_button_label" => "Start Process %s",
"create_then_start_process_button_label" => "Tambah Dan Start Process %s",
"submit_task_button_label" => "Submit Task %s",
"submit_task_confirm_message" => "Apakah anda yakin ingin submit task untuk data ini?",
"submit_task_confirm_header" => "Submit Task",
"submit_task_confirm_conirm_label" => "Lanjutkan",
"submit_task_confirm_cancel_label" => "Batal",
"delete_confirm_message" => "Apakah anda yakin ingin menghapus data ini?1",
"delete_confirm_header" => "Hapus Data",
"delete_confirm_conirm_label" => "Lanjutkan",
"delete_confirm_cancel_label" => "Batal",
"start_process_confirm_message" => "Apakah anda yakin ingin submit process untuk data ini?",
"start_process_confirm_header" => "Submit Process",
"start_process_confirm_conirm_label" => "Lanjutkan",
"start_process_confirm_cancel_label" => "Batal",
"field_list_description" => "Field Dari %s",
"upload_action_message" => "Klik Untuk Mengunggah Atau Drag File",
"upload_file_type_message" => "File yang bisa diupload adalah ",
"upload_uploading_message" => "Mengunggah...",
"upload_upload_error_header" => "Gagal Menggungah",
"upload_upload_error_message" => "Terjadi kesalahan ketika mengunggah file",
"upload_file_type_error_message" => "Type file tidak bisa di upload ",
"upload_file_size_exceed_header" => "Size File Terlalu Besar",
"upload_file_size_exceed_message" => "Size file lebih besar dari yang di perbolehkan",
"pagination_info" => "Memunculkan data dari %s sampai %s total %s",
"empty_data_message" => "Tidak ada data yang ditampilkan",
"filter_title" => "Filter",
"filter_submit" => "Filter",
"filter_reset" => "Clear Filter",
"filter_cancel" => "Kembali",
],
"default" => "ID"
];
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (!(Schema::hasTable('ak_activity_log'))) {
Schema::create('ak_activity_log', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('class_name', 255)->nullable();
$table->string('action')->nullable();
$table->string('business_key')->nullable();
$table->string('entity_id')->nullable();
$table->string('log_type')->nullable();
$table->text('payload')->nullable();
$table->string('remarks', 255)->nullable();
$table->string('status_after', 255)->nullable();
$table->string('status_before', 255)->nullable();
$table->string('act_by_name', 255)->nullable();
$table->string('decision', 255)->nullable();
$table->string('decision_remark', 255)->nullable();
$table->boolean('latest')->nullable();
$table->timestamps();
$table->userstamps();
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('ak_activity_log');
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if (!Schema::hasTable('ak_notif_users')) {
Schema::create('ak_notif_users', function (Blueprint $table) {
$table->id();
$table->string("title");
$table->string("message");
$table->text("payload");
$table->string("url_to");
$table->string("user_id");
$table->date("read_at")->nullable();
$table->date("sent_at")->nullable();
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('ak_notif_users');
}
};
<?php
namespace App\Http\Controllers\Api\Bpmn;
use App\Models\Bpmn\{Class};
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Laravolt\Crud\BpmnController;
use Laravolt\Crud\BpmnModel;
use App\Services\Bpmn\{Module}\{Class}Service;
use Laravolt\Crud\BpmnService;
class {Class}Controller extends BpmnController
{
public function model(): BpmnModel
{
return new {Class}();
}
/**
* @return BpmnService
*/
public function service(): BpmnService
{
return new {Class}Service($this->model(), $this->user);
}
}
<?php
namespace App\Jobs\{bpmn};
use Laravolt\Camunda\Dto\ExternalTask;
use Laravolt\Camunda\Http\ExternalTaskClient;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class {Class}Job implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct(
public string $workerId,
public ExternalTask $task
) {
}
/**
* Execute the job.
*/
public function handle()
{
$status = ExternalTaskClient::complete($this->task->id, $this->workerId);
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('{table}', function (Blueprint $table) {
$table->id();
$table->string("process_instance_id")->nullable();
$table->timestamps();
$table->userstamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('{table}');
}
};
<?php
namespace App\Models\Bpmn;
use Laravolt\Crud\BpmnModel;
use Laravolt\Crud\Enum\AutoMode;
class {Class} extends BpmnModel
{
protected $table = '{table}';
protected string $path = "{path}";
public AutoMode $filterMode = AutoMode::BLACKLIST;
public AutoMode $searchMode = AutoMode::BLACKLIST;
public AutoMode $sortMode = AutoMode::BLACKLIST;
}
<?php
namespace App\Services\Bpmn\{Module};
use App\Models\Bpmn\{Module}\{Class};
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Laravolt\Crud\BpmnService;
class {Class}Service extends BpmnService
{
}
<?php
namespace App\Http\Controllers\Api\Bpmn\{Module};
use App\Models\Bpmn\{Module}\{Class};
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Laravolt\Crud\UserTaskController;
use Laravolt\Crud\UserTaskModel;
use App\Services\Bpmn\{Module}\{Class}Service;
use Laravolt\Crud\UserTaskService;
class {Class}Controller extends UserTaskController
{
public function model(): UserTaskModel
{
return new {Class}();
}
/**
* @return UserTaskService
*/
public function service(): UserTaskService
{
return new {Class}Service($this->model(), $this->user);
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('{table}', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger("business_key")->nullable();
$table->timestamps();
$table->userstamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('{table}');
}
};
<?php
namespace App\Models\Bpmn\{Module};
use Laravolt\Crud\UserTaskModel;
use Laravolt\Crud\Enum\AutoMode;
class {Class} extends UserTaskModel
{
protected $table = '{table}';
protected string $path = "{path}";
public AutoMode $filterMode = AutoMode::BLACKLIST;
public AutoMode $searchMode = AutoMode::BLACKLIST;
public AutoMode $sortMode = AutoMode::BLACKLIST;
}
<?php
namespace App\Services\Bpmn\{Module};
use App\Models\Bpmn\{Module}\{Class};
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Laravolt\Crud\UserTaskService;
class {Class}Service extends UserTaskService
{
}
<?php
namespace App\Http\Controllers\Api\Crud\{Module};
use App\Models\{Class};
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Laravolt\Crud\ApiCrudController;
use Laravolt\Crud\CrudModel;
use App\Services\{Module}\{Class}Service;
use Laravolt\Crud\CrudService;
class {Class}Controller extends ApiCrudController
{
public function model(): CrudModel
{
return new {Class}();
}
/**
* @return CrudService
*/
public function service(): CrudService
{
return new {Class}Service($this->model(), $this->user);
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('{table}', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->userstamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('{table}');
}
};
<?php
namespace App\Models;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\AutoMode;
class {Class} extends CrudModel
{
protected $table = '{table}';
protected string $path = "{path}";
public AutoMode $filterMode = AutoMode::BLACKLIST;
public AutoMode $searchMode = AutoMode::BLACKLIST;
public AutoMode $sortMode = AutoMode::BLACKLIST;
}
<?php
namespace App\Services\{Module};
use App\Models\{Module}\{Class};
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Laravolt\Crud\CrudService;
class {Class}Service extends CrudService
{
}
<?php
namespace App\Http\Controllers\Api\Crud\{Module};
use App\Services\{Module}\{Class}Service;
use Laravolt\Crud\Traits\CanFormatResource;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Request as HttpRequest;
class {Class}Controller
{
use CanFormatResource;
public function index(HttpRequest $request) : JsonResource
{
return $this->collection((new {Class}Service())->index($request));
}
}
<?php
namespace App\Services\{Module};
use Illuminate\Http\Request as HttpRequest;
use Illuminate\Support\Collection;
class {Class}Service
{
public function index(HttpRequest $request ) : Collection {
return collect($request->all());
}
}
<?php
namespace App\Http\Controllers\Api\Crud\{Module};
use App\Models\{Module}\{Class};
use App\Services\{Module}\{Class}Service;
use Laravolt\Crud\ApiReadonlyController;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\CrudService;
class {Class}Controller extends ApiReadonlyController
{
public function model(): CrudModel
{
return new {Class}();
}
/**
* @return CrudService
*/
public function service(): CrudService
{
return new {Class}Service($this->model(), $this->user);
}
}
<?php
namespace App\Models\{Module};
use Laravolt\Crud\CrudModel;
class {Class} extends CrudModel
{
protected $table = '{table}';
protected string $path = "{path}";
}
<?php
namespace App\Services\{Module};
use App\Models\{Module}\{Class};
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Laravolt\Crud\CrudService;
class {Class}Service extends CrudService
{
}
<x-volt-app title="{{ request()->route()->getName() }}">
<div
id="crudIndex"
data-base-url="{{ config('api.url') }}/{{ \Illuminate\Support\Str::of(request()->route()->getName())->before('.') }}"
data-title="{{ request()->route()->getName() }}"
data-description=""
>
</div>
</x-volt-app>
<?php
namespace Laravolt\Crud;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Laravolt\Crud\BulkActions\BulkCreate;
use Laravolt\Crud\BulkActions\BulkDelete;
use Laravolt\Crud\BulkActions\BulkReplace;
use Laravolt\Crud\BulkActions\BulkSubmitTask;
use Laravolt\Crud\BulkActions\BulkUpdate;
use Laravolt\Crud\Contracts\CrudControllerContract;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Contracts\UpdateRequestContract;
use Laravolt\Crud\Response\BulkResponse;
use Laravolt\Crud\Response\DeleteFail;
use Laravolt\Crud\Response\DeleteSuccess;
use Laravolt\Crud\Response\NotFound;
use Spatie\RouteDiscovery\Attributes\Route;
abstract class ApiCrudController extends ApiReadonlyController implements CrudControllerContract
{
protected string $storeRequest = CrudRequest::class;
protected string $updateRequest = CrudRequest::class;
private array $bulkActions = [
'create' => BulkCreate::class,
'update' => BulkUpdate::class,
'delete' => BulkDelete::class,
'replace' => BulkReplace::class,
'submitTask' => BulkSubmitTask::class,
];
public function __construct()
{
app()->bind(StoreRequestContract::class, $this->storeRequest);
app()->bind(UpdateRequestContract::class, $this->updateRequest);
}
/**
* POST. Create Data
*/
public function store(StoreRequestContract $request): JsonResource
{
// $this->authorize('create', $this->model());
$this->guard("CREATE");
$model = $this->service->create($request);
$model->refresh();
return $this->single($model);
}
/**
* POST. PUT. Update Data
*/
#[Route(method: ['POST', 'PUT'])]
public function update(UpdateRequestContract $request, mixed $id): JsonResource
{
try {
$this->guard("UPDATE");
// $this->authorize('update', $this->service->find($id));
$model = $this->service->update($id, $request);
return $this->single($model);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
}
}
/**
* DELETE. Data
*/
public function destroy(mixed $id): JsonResource
{
try {
$model = $this->service->find($id);
$this->guard("DELETE");
// $this->authorize('delete', $model);
$deleted = $this->service->delete($model);
return $deleted ? new DeleteSuccess(null) : new DeleteFail(null);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
}
}
public function rules(): JsonResource
{
return $this->collection($this->service->rules());
}
/**
* POST. Bulk Data
*/
#[Route(method: 'POST')]
public function bulk(): JsonResource
{
$actions = $this->bulkActions();
$validActions = request()->only(array_keys($actions));
$this->validateBulkActions($validActions);
$result = [];
foreach ($validActions as $action => $payload) {
$bulkClass = $actions[$action] ?? false;
if ($bulkClass) {
$result[$action] = (new $bulkClass)($payload, $this->model());
}
}
return (new BulkResponse($result))
->withInvalid(request()->except(array_keys($validActions)));
}
protected function bulkActions(): array
{
return $this->bulkActions;
}
/**
* @throws \Laravolt\Crud\Response\AlurKerja\ValidationException
*/
protected function validateBulkActions(array $validActions)
{
// Check number of bulk operations
$validator = Validator::make(
['count' => collect($validActions)->flatten(1)->count()],
['count' => ['numeric', 'max:20']],
[],
['count' => 'number of actions']
);
if ($validator->fails()) {
throw new \Laravolt\Crud\Response\AlurKerja\ValidationException($validator);
}
return true;
}
}
<?php
namespace Laravolt\Crud;
enum ApiFormat
{
case Laravolt;
case AlurKerja;
}
<?php
namespace Laravolt\Crud;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection as BaseResourceCollection;
use Illuminate\Support\Facades\Auth;
use Laravolt\Crud\Response\NotFound;
use Laravolt\Rbac\Contracts\HasRoleAndPermission;
abstract class ApiReadonlyController extends BaseController
{
public function guard($action)
{
// $user = $this->user;
// abort_if($user === null && !config("rbac.guest_access"), 401, 'You have no authorization.');
// if (is_subclass_of($user, '\Laravolt\Rbac\Contracts\HasRoleAndPermission')) {
// if ($user !== null && !$user->getActionPermissionFromUrl(\request()->getPathInfo(), $action)) {
// abort(405, 'You have no access.');
// }
// }
return true;
}
/**
* GET. List Data
*
* @queryParam search string For searching data.
* @queryParam filter string For filtering data.
* @queryParam sort string For sorting data.
*/
public function index(Request $request): BaseResourceCollection
{
// $this->authorize('viewAny', $this->model());
$this->guard("LIST");
return $this->collection($this->service->get($request));
}
/**
* GET. Scope
*/
public function scope(Request $request, $scope): JsonResource
{
try {
return $this->collection($this->service->getByScope($request, $scope));
} catch (\BadMethodCallException $e) {
return new NotFound($e);
}
}
/**
* GET. Detail Data
*/
public function show(Request $request, mixed $id): JsonResource
{
try {
$this->guard("SHOW");
$model = $this->service->find($id);
// $this->authorize('view', $model);
return $this->single($model);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
}
}
/**
* GET. Spec
*/
public function spec(): JsonResource
{
return $this->single($this->service->spec());
}
}
<?php
namespace Laravolt\Crud;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Routing\Controller;
use Laravolt\Crud\Contracts\CrudUser;
use Laravolt\Crud\Traits\CanFormatResource;
abstract class BaseController extends Controller
{
use AuthorizesRequests;
use CanFormatResource;
protected CrudService $service;
protected CrudUser|null $user = null;
abstract public function model(): CrudModel;
protected function service(): CrudService
{
return new CrudService($this->model(), $this->user);
}
protected function isGuest(): bool|int|string
{
$currentMiddleware = \request()?->route()?->gatherMiddleware() ?? [];
return array_search('allow_guest', $currentMiddleware, true);
}
public function callAction($method, $parameters)
{
$this->user = auth()->user();
$this->service = $this->service();
return parent::callAction($method, $parameters);
}
}
<?php
namespace Laravolt\Crud;
use App\Models\Camunda\BpmnTask;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Cache;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Camunda\Http\TaskClient;
use Laravolt\Crud\Contracts\BpmnControllerContract;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Exceptions\CrudException;
use Laravolt\Crud\Helper\BpmnXmlHelper;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Response\DomainViolationResponse;
use Laravolt\Crud\Response\NoContent;
use Laravolt\Crud\Response\NotFound;
use Laravolt\Crud\Traits\CanFormatBpmnVariables;
use Spatie\RouteDiscovery\Attributes\Route;
abstract class BpmnController extends ApiCrudController implements BpmnControllerContract
{
use CanFormatBpmnVariables, BpmnXmlHelper, TableSpecHelper;
abstract public function model(): BpmnModel;
protected function service(): BpmnService
{
return new BpmnService($this->model(), $this->user);
}
/**
* POST. [BPMN] Start Process
*/
#[Route(method: 'POST')]
public function start(StoreRequestContract $request): JsonResource
{
$model = $this->service()->start($request);
return $this->single($model);
}
/**
* POST. [BPMN] Start Existing Process
*/
#[Route(method: 'POST', uri: '{id}/start')]
public function startExisting(Request $request, mixed $modelId): JsonResource
{
$result = $this->service()->startExisting($modelId, $request->all());
return $this->single($result);
}
public function myTask(Request $request): JsonResource
{
$result = $this->service()->getMyTask($request);
return $this->collection($result);
}
public function availableTask(Request $request): JsonResource
{
$result = $this->service()->getAvailableTask($request);
return $this->collection($result);
}
/**
* GET. [BPMN] Diagram
*/
public function xml()
{
$model = $this->model();
try {
$xml = ProcessDefinitionClient::xml(key: $model->processDefinitionKey());
return response(
$xml,
200,
['Content-Type' => 'application/xml']
);
} catch (\TypeError $error) {
return new NotFound(
new CrudException(sprintf('Process definition key "%s" not found', $model->processDefinitionKey()))
);
}
}
/**
* GET. [BPMN] Status Task
*/
public function statistic(): JsonResource
{
$model = $this->model();
$statistic = [];
$processName = $model->processDefinitionKey();
$node = $model->pareseBpmnFromProcessDefinition($processName);
$tasks = Cache::remember("all_task_of_$processName", 5, function () use ($processName) {
$tasks = TaskClient::unfinishedTask([
"processDefinitionKey" => $processName
]);
return $tasks;
});
foreach ($node as $usertask) {
$taskInstance = $usertask["id"];
$statistic[$taskInstance] = collect($tasks)->filter(function ($data) use ($taskInstance) {
return $taskInstance == $data->taskDefinitionKey;
})->count();
}
return JsonResource::make(['tasks' => $statistic]);
}
}
<?php
namespace Laravolt\Crud;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Crud\Helper\BpmnXmlHelper;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Models\ActivityLog;
use Laravolt\Crud\Models\BpmnTask;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Facades\Cache;
use Laravolt\Crud\Sys\ActivityLog\AkActivityLog;
abstract class BpmnModel extends CrudModel
{
use BpmnXmlHelper, TableSpecHelper;
protected string $businessKey = 'business_key';
protected array $filterableColumns = [
'bpmnTasks.task_definition_key',
'bpmnTasks.process_definition_key',
];
protected string $processInstanceKey = 'process_instance_id';
protected array $processVariables = [];
protected bool $isBpmn = true;
protected array $transientProcessVariables = [];
public function getProcessInstanceKey(): string
{
return $this->processInstanceKey;
}
public function getBusinessKeyFieldName(): string
{
return $this->businessKey;
}
public function getProcessInstanceId(): string|null
{
return $this->getAttribute($this->getProcessInstanceKey());
}
public function setProcessInstanceId(string $id): static
{
$this->{$this->getProcessInstanceKey()} = $id;
return $this;
}
public function processDefinitionKey(): string
{
return (new \ReflectionClass(static::class))->getShortName();
}
public function processDefinitionLabel(): string
{
return $this->toWord((new \ReflectionClass(static::class))->getShortName());
}
//
// public function bpmnTasks(): HasMany
// {
// if (config("services.camunda.on_premise")) {
// return [];
// } else {
// return $this->hasMany(BpmnTask::class, 'process_instance_id', 'process_instance_id');
// }
// }
public function getProcessVariables(): array
{
return $this->processVariables;
}
function getUserTask(): array
{
$processName = $this->processDefinitionKey();
return $this->pareseBpmnFromProcessDefinition($processName);
}
public function getTransientProcessVariables(): array
{
return $this->transientProcessVariables;
}
public function toProcessVariables(array $data = []): array
{
$variables = [];
foreach ($this->getProcessVariables() as $var) {
$value = $data[$var] ?? $this->getAttribute($var) ?? null;
if ($value !== null) {
$variables[$var] = $value;
}
}
return $variables;
}
// public function logs(): HasMany
// {
// return $this->hasMany(AkActivityLog::class, "business_key" , "id" )->orderBy("created_at", "desc");
// }
/**
* @param string $processName
* @return mixed
*/
public function pareseBpmnFromProcessDefinition(string $processName)
{
return Cache::rememberForever('user_task_list'.$processName, function () use ($processName) {
$xml_string = ProcessDefinitionClient::xml(key: $processName);
$doc = new \DOMDocument();
$doc->loadXML($xml_string);
unset($xml_string);
$childNode = [];
foreach ($doc->childNodes as $node) {
array_push($childNode, $this->parseNode($node));
}
$node = $this->getNodeCollection($childNode, ["bpmn:userTask"]);
return $node->map(function ($usertask) use ($processName) {
return [
'type' => $usertask["name"],
'id' => $usertask["attributes"]['id'],
'label' => $usertask["attributes"]['name'],
'url' => ($this->getPath() ?? "api/bpmn/cuti/" . $this->toSnakeCase($processName)) . "/" . $this->toUrlPath($usertask["attributes"]["id"]),
];
})->toArray();
});
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Camunda\Http\ProcessInstanceClient;
use Laravolt\Camunda\Http\TaskClient;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Response\DomainViolationResponse;
use Laravolt\Crud\Sys\ActivityLog\AkActivityLog;
use Laravolt\Crud\Traits\CanFormatBpmnVariables;
class BpmnService extends CrudService
{
use CanFormatBpmnVariables, TableSpecHelper;
public function appendQuery($query)
{
$filter = \request('filter', []);
// prevent filter value is array not string
$filter = is_array($filter) ? $filter : [];
if (array_key_exists("task_definition_key", $filter)) {
$taskFilter = $filter['task_definition_key'];
$processName = $this->model->processDefinitionKey();
$tasks = Cache::remember("all_task_of_$processName" . "_" . $taskFilter, 5, function () use ($processName, $taskFilter) {
$tasks = TaskClient::unfinishedTask([
"processDefinitionKey" => $processName,
"taskDefinitionKey" => $taskFilter
]);
return $tasks;
});
$processInstanceId = collect($tasks)->map(function ($task) {
return $task->processInstanceId;
});
$query->whereIn("process_instance_id", $processInstanceId);
}
if (array_key_exists("process_isntance_id", $filter)) {
$processInstanceId = $filter['proces_isntance_id'];
$query->whereIn("process_instance_id", $processInstanceId);
}
return parent::appendQuery($query); // TODO: Change the autogenerated stub
}
public function get(Request $request): LengthAwarePaginator
{
$data = parent::get($request); // TODO: Change the autogenerated stub
$processInstance = $this->getProcessInstance($data);
$tasks = collect(TaskClient::getByProcessInstanceIds(ids: $processInstance->toArray()));
$data->getCollection()->transform(function ($processInstance) use ($tasks) {
$processInstance['available_task'] = $tasks->filter(function ($taskInstance) use ($processInstance) {
return $taskInstance->processInstanceId == $processInstance->process_instance_id;
})->toArray();
if(count($processInstance['available_task']) == 0){
$processInstance['available_task'] = $this->getSubPreocessTask($processInstance['id']);
}
return $processInstance;
});
return $data;
}
public function getSubPreocessTask ($busniess_key) {
$processInstanceId = Cache::remember("subprocess_process_instance_id_" . $busniess_key , 30*60 , function () use($busniess_key){
return collect(ProcessInstanceClient::get(['businessKey' => $busniess_key]))->map(function ($processInstance) {
return $processInstance->id;
});
} );
$tasks = collect(TaskClient::getByProcessInstanceIds(ids: $processInstanceId->toArray()));
return $tasks;
}
public function find(mixed $id): CrudModel
{
$model = parent::find($id);
if ($model->process_instance_id) {
$tasks = collect(TaskClient::getByProcessInstanceId($model->process_instance_id));
$model->available_task = $tasks->filter(function ($taskInstance) use ($model) {
return $taskInstance->processInstanceId == $model->process_instance_id;
});
if (count($model->available_task) == 0) {
$model->available_task = $this->getSubPreocessTask($model->id);
}
}
return $model;
}
public function getMyTask(Request $request)
{
$user_id = $request->user_id;
$tasks = TaskClient::getByAssignedAndProcessInstanceId($user_id);
$tasks = $this->parseTask($tasks);
return $tasks->paginate(10);
}
public function getAvailableTask(Request $request)
{
$tasks = TaskClient::get([
"processDefinitionKey" => $this->model->processDefinitionKey(),
"unassigned" => true,
]);
$tasks = $this->parseTask($tasks);
return $tasks;
}
public function prepareCreateData($request)
{
$model = $this->model;
$allowedPayload = array_diff(array_keys($request->all()), $model->getTransientProcessVariables());
$payload = $request->only($allowedPayload);
return $payload;
}
public function afterStartHook(BpmnModel $model, Request $request): BpmnModel
{
return $model;
}
public function afterStartExistingHook(BpmnModel $model, array $payload): BpmnModel
{
return $model;
}
public function storeData(StoreRequestContract|FormRequest $request): BpmnModel
{
return $this->create($request);
}
public function start(StoreRequestContract|FormRequest $request): BpmnModel
{
return DB::transaction(function () use ($request) {
/** @var \Laravolt\Crud\BpmnModel $model */
$model = $this->storeData($request);
$variable = $model->toProcessVariables($request->all());
$processInstance = ProcessDefinitionClient::start(
key: $model->processDefinitionKey(),
businessKey: $model->getKey(),
variables: $this->toBpmnVariables($variable)
);
$model->setProcessInstanceId($processInstance->id)->save();
$model = $this->afterStartHook($model, $request);
// $tasks = collect(TaskClient::getByProcessInstanceId($processInstance->id));
//
// $data = new stdClass();
// $data->id = 1;
// $remarks = 'Test remarks';
// $status_fter = 'Test status after';
// $status_befor = 'Test status before';
// $act_by_name = 'Test actor name';
// $decision = 'Test decision';
// $decisionremark = 'Test decision remark';
AkActivityLog::createBpmnLog(
$model,
$model->id,
"NEW",
"Tobe defined",
json_encode($variable),
"Tobe defined",
);
return $model;
});
}
public function startExisting(mixed $id, array $payload)
{
/** @var \Laravolt\Crud\BpmnModel $model */
$model = $this->find($id);
// Sudah pernah di-start sebelumnya
if ($model->getProcessInstanceId()) {
return new DomainViolationResponse(
sprintf(
'Bpmn model with id %s already started with process instance ID %s',
$model->getKey(),
$model->getProcessInstanceId()
)
);
}
return DB::transaction(function () use ($model, $payload) {
$processInstance = ProcessDefinitionClient::start(
key: $model->processDefinitionKey(),
businessKey: $model->getKey(),
//TODO: how to submit process variables only
variables: $this->toBpmnVariables($model->toProcessVariables($payload))
);
$model->setProcessInstanceId($processInstance->id)->save();
$model = $this->afterStartExistingHook($model, $payload);
AkActivityLog::createBpmnLog(
$model,
$model->id,
"NEW",
"Tobe defined",
json_encode($model->toProcessVariables(\request()->all())),
"Tobe defined",
);
return $model;
});
}
public function submitTask(string $taskId, BpmnModel $model, Request $request): void
{
$task = TaskClient::find($taskId);
if ($task->processInstanceId !== $model->getProcessInstanceId()) {
throw new \DomainException(
sprintf(
'Task ID %s does not belong to model ID %s',
$taskId,
$model->getKey()
)
);
}
DB::transaction(function () use ($task, $model, $request) {
// Only update attributes defined in rules to avoid undesired user input
// TODO how to validate request for current task
$variables = $request->only($model->getProcessVariables());
$model->fill($request->only(array_keys($model->rules($request))));
if ($model->save()) {
$variables = ['_id' => $model->getKey()] + $variables;
$method = 'variableFor' . $task->taskDefinitionKey;
if (method_exists($model, $method)) {
$variables = $model->$method($variables);
}
// Log::info(
// 'Submit Task',
// ['pid' => $task->processInstanceId, 'tid' => $task->id, 'variables' => $variables]
// );
TaskClient::submit($task->id, $this->toBpmnVariables($variables));
}
$tasks = collect(TaskClient::getByProcessInstanceId($task->processInstanceId));
// $model->logs()->create([
// 'decision' => collect($variables),
// 'status_before' => $task->taskDefinitionKey,
// 'status_after' => $tasks->pluck('taskDefinitionKey'),
// ]);
});
}
/**
* @param LengthAwarePaginator $data
* @return mixed
*/
public function getProcessInstance(LengthAwarePaginator $data)
{
$processInstance = [];
$processInstance = $data->getCollection()->map(function ($processData) {
return $processData->process_instance_id;
})->filter(function ($processData) {
return $processData != null;
});
return $processInstance;
}
/**
* @param array $tasks
* @return \Illuminate\Support\Collection
*/
public function parseTask(array $tasks): \Illuminate\Support\Collection
{
$tasks = collect($tasks);
$processInstanceIds = $tasks->map(fn ($e) => $e->processInstanceId)->join(',');
// get all of process instance by process instance id in task api, for the sub process task
$processInstances = collect(ProcessInstanceClient::get(['processInstanceIds' => $processInstanceIds]));
$businessKeys = $processInstances->map(fn ($e) => $e->businessKey);
$userTask = collect($this->model->getUserTask());
$models = $this->model
->newQuery()
->with($this->autoLoadRelations)
->wherein("id", $businessKeys->toArray())
->get();
$processDefinitionKey = $this->model->processDefinitionKey();
$processDefinitionLabel = $this->model->processDefinitionLabel();
$tasks = $tasks->map(function ($task) use ($models, $processInstances, $userTask, $processDefinitionKey, $processDefinitionLabel) {
$data = [];
$task = $task->toArray();
foreach (array_keys($task) as $value) {
$data[$this->toSnakeCase($value)] = $task[$value];
}
// get business key from list of process instances
$businessKey = $processInstances->firstWhere('id', $task['processInstanceId'])->businessKey;
$data['process_instance'] = $models->firstWhere('id', $businessKey);
$data['process_definition_key'] = $processDefinitionKey;
$data['process_definition_label'] = trim($processDefinitionLabel);
$data['user_task_mapping'] = $userTask
->filter(fn ($userTask) => $userTask['id'] == $task['taskDefinitionKey'])
->first();
return $data;
});
$tasks = $tasks
->filter(fn ($item) => $item['user_task_mapping'] != null)
->values();
return $tasks;
}
public function delete(mixed $model): ?bool
{
return DB::transaction(function () use ($model) {
ProcessInstanceClient::delete($model->process_instance_id);
return parent::delete($model);
});
}
}
<?php
namespace Laravolt\Crud\BulkActions;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Traits\CanHandleMedia;
class BulkCreate
{
use AuthorizesRequests;
use CanHandleMedia;
/**
* @throws \Throwable
*/
public function __invoke(array $rawPayload, CrudModel $model): array
{
// $this->authorize('create', $model);
$rules = collect($model->rules(request()))->mapWithKeys(fn ($rule, $key) => ["*.$key" => $rule])->toArray();
$validated = Validator::make($rawPayload, $rules)->validate();
return DB::transaction(function () use ($model, $validated, $rawPayload) {
$result = ['data' => []];
$successCounter = 0;
foreach ($validated as $index => $payload) {
/** @var CrudModel $instance */
$instance = app(get_class($model));
$request = clone request();
$request->replace($rawPayload[$index] ?? []);
if ($instance->fill($payload + $instance->defaultPayload($request))->save()) {
$this->processMediaable($request, $instance);
// $this->processNexcloudable($request, $instance);
$successCounter++;
$result['data'][] = $instance->toArray();
}
}
$result['total'] = $successCounter;
return $result;
});
}
}
<?php
namespace Laravolt\Crud\BulkActions;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Laravolt\Crud\CrudModel;
class BulkDelete
{
use AuthorizesRequests;
/**
* @throws \Throwable
*/
public function __invoke(array $rawPayload, CrudModel $model): int
{
return DB::transaction(function () use ($rawPayload, $model) {
$successCounter = 0;
$models = $model->newQuery()->findMany(collect($rawPayload)->pluck('id'))->keyBy('id');
/** @var CrudModel $item */
foreach ($models as $item) {
// $this->authorize('delete', $item);
$successCounter += (int) $item->delete();
}
return $successCounter;
});
}
}
<?php
namespace Laravolt\Crud\BulkActions;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Laravolt\Crud\CrudModel;
class BulkReplace
{
use AuthorizesRequests;
/**
* @throws \Throwable
*/
public function __invoke(array $rawPayload, CrudModel $model): int
{
return DB::transaction(function () use ($rawPayload, $model) {
$successCounter = 0;
$payload = collect($rawPayload);
$models = $model->newQuery()->findMany($payload->pluck('id'))->keyBy('id');
/** @var CrudModel $item */
foreach ($models as $item) {
// $this->authorize('update', $item);
$toBeReplaced = $payload->where('id', $item->getKey())->first();
unset($toBeReplaced['id']);
foreach ($toBeReplaced as $relation => $bulkData) {
$ids = collect($bulkData)->pluck('id')->filter();
$successCounter += $item->{$relation}()->whereNotIn('id', $ids)->delete();
foreach ($bulkData as $data) {
$id = $data['id'] ?? false;
if ($id) {
$successCounter += $item->{$relation}()
->where('id', $data['id'])
->update(Arr::except($data, 'id'));
} else {
unset($data['id']);
if ($item->{$relation}()->create($data)) {
$successCounter++;
}
}
}
}
}
return $successCounter;
});
}
}
<?php
namespace Laravolt\Crud\BulkActions;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Laravolt\Crud\BpmnService;
use Laravolt\Crud\CrudModel;
class BulkSubmitTask
{
use AuthorizesRequests;
/**
* @throws \Throwable
*/
public function __invoke(array $rawPayload, CrudModel $model): int
{
return DB::transaction(function () use ($rawPayload, $model) {
$successCounter = 0;
$payload = collect($rawPayload);
$models = $model->newQuery()->findMany($payload->pluck('id'))->keyBy('id');
$service = new BpmnService($model, auth()->user());
/** @var \Laravolt\Crud\BpmnModel $model */
foreach ($models as $model) {
// $this->authorize('update', $model);
}
foreach ($rawPayload as $payload) {
$request = clone request();
$request->replace($payload);
$service->submitTask($payload['task_id'], $model, $request);
}
return $successCounter;
});
}
}
<?php
namespace Laravolt\Crud\BulkActions;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Laravolt\Crud\CrudModel;
class BulkUpdate
{
use AuthorizesRequests;
/**
* @throws \Throwable
*/
public function __invoke(array $rawPayload, CrudModel $model): int
{
// TODO: dari FE tidak mengirim semua data, perlu mekanisme validation yang lebih baik
// $rules = collect($model->rules(request()))->mapWithKeys(fn ($rule, $key) => ["*.$key" => $rule])->toArray();
// \Validator::make($rawPayload, $rules)->validate();
return DB::transaction(function () use ($rawPayload, $model) {
$successCounter = 0;
$models = $model->newQuery()->findMany(collect($rawPayload)->pluck('id'))->keyBy('id');
foreach ($rawPayload as $payload) {
$payload = collect($payload);
$item = $models[$payload->get('id')] ?? null;
if ($item) {
// $this->authorize('update', $item);
$successCounter += (int) $item->update($payload->except(['id'])->toArray());
}
}
return $successCounter;
});
}
}
<?php
namespace Laravolt\Crud\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateBpmnApi extends Command
{
use GeneratorTrait, TableSpecHelper;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:bpmn:api {model} {table?} {module?} {replace?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$replace = $this->argument('replace') == "true";
$model = $this->argument('model');
if (!$this->argument('table')) {
$table = "";
while ($table == "") {
$table = $this->ask("What is the table name?", $this->toSnakeCase($model . "s") );
}
} else {
$table = $this->argument('table');
}
if (!$this->argument('module')) {
$module = "";
while ($module == "") {
$module = $this->ask("What is the module name ? ", $model);
}
} else {
$module = $this->argument('module');
}
$class = $model;
$controllerPath = app_path() . "/Http/Controllers/Api/Bpmn/";
$this->checkAndGenerateFolder($controllerPath);
$servicePath = app_path() . "/Services/Bpmn/" . $module . "/";
$this->checkAndGenerateFolder($servicePath);
$modelPath = app_path() . "/Models/Bpmn/";
$this->checkAndGenerateFolder($modelPath);
$migrationPath = base_path() . "/database/migrations/" ;
//
// $this->info("Creating Model $class");
// $modelStub = file_get_contents(__DIR__ . '/../../resources/stubs/Crud/Model.php.stub');
// $modelStub = str_replace("{Class}", $class, $modelStub);
// $modelStub = str_replace("{Module}", $module, $modelStub);
// $modelStub = str_replace("{table}", $table, $modelStub);
// file_put_contents(app_path() . "/Models/$class.php", $modelStub);
$this->info("Creating Model $class");
$this->generateFile($class, __DIR__ . '/../../resources/stubs/Bpmn/PROCESS/Model.php.stub', app_path() . "/Models/Bpmn/", [
"Class" => $class,
"Module" => $module,
"table" => $table,
"path" => "/api/" . $this->toUrlPath($class)
], $replace);
$this->info("Creating Controller $class");
$this->generateFile($class . "Controller", __DIR__ . '/../../resources/stubs/Bpmn/PROCESS/Controller.php.stub', $controllerPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
$this->info("Creating Services $class");
$this->generateFile($class . "Service", __DIR__ . '/../../resources/stubs/Bpmn/PROCESS/Service.php.stub', $servicePath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
$this->info("Running Optimize $class");
$this->call('optimize');
$this->info("Running Creating Migration $class");
$class = Carbon::now()->addSecond(-2)->format("Y_m_d_His"). "_create_{$table}_table";
$this->generateFile($class , __DIR__ . '/../../resources/stubs/Bpmn/PROCESS/Migration.php.stub', $migrationPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Illuminate\Console\Command;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateControllerWithService extends Command
{
use GeneratorTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:controller:api {model}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$model = $this->argument('model');
$table = "";
while ($table == "") {
$table = $this->ask("What Is ControllerName (Without Controller Ex: Report )", $model);
}
$module = "";
while ($module == "") {
$module = $this->ask("What is the module name ? ", $model);
}
$this->info("Creating Module $module");
$class = $model;
$controllerPath = app_path() . "/Http/Controllers/Api/Crud/" . $module . "/";
$this->checkAndGenerateFolder($controllerPath);
$servicePath = app_path() . "/Services/" . $module . "/";
$this->checkAndGenerateFolder($servicePath);
$this->info("Creating Controller $class");
$this->generateFile($class . "Controller", __DIR__ . '/../../resources/stubs/SimpleController/Controller.php.stub', $controllerPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Creating Services $class");
$this->generateFile($class . "Service", __DIR__ . '/../../resources/stubs/SimpleController/Service.php.stub', $servicePath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Running Optimize $class");
$this->call('optimize');
$this->call('make:test' , [
'name' => $class."Controller".'Test'
]);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Illuminate\Console\Command;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateCrudApi extends Command
{
use GeneratorTrait, TableSpecHelper;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:crud:api {model}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$model = $this->argument('model');
$table = "";
while ($table == "") {
$table = $this->ask("What is the table name?", $this->toSnakeCase($model . "s"));
}
$module = "";
while ($module == "") {
$module = $this->ask("What is the module name ? ", $model);
}
$class = $model;
$controllerPath = app_path() . "/Http/Controllers/Api/Crud/" . $module . "/";
$this->checkAndGenerateFolder($controllerPath);
$servicePath = app_path() . "/Services/" . $module . "/";
$this->checkAndGenerateFolder($servicePath);
$modelPath = app_path() . "/Models/Bpmn/" . $module;
$this->checkAndGenerateFolder($modelPath);
//
//
// $this->info("Creating Model $class");
// $modelStub = file_get_contents(__DIR__ . '/../../resources/stubs/Crud/Model.php.stub');
// $modelStub = str_replace("{Class}", $class, $modelStub);
// $modelStub = str_replace("{Module}", $module, $modelStub);
// $modelStub = str_replace("{table}", $table, $modelStub);
// file_put_contents(app_path() . "/Models/$class.php", $modelStub);
$migrationPath = base_path() . "/database/migrations/";
$this->info("Creating Model $class");
$this->generateFile($class, __DIR__ . '/../../resources/stubs/Crud/Model.php.stub', app_path() . "/Models/", [
"Class" => $class,
"Module" => $module,
"table" => $table,
"path" => "/api/" . $this->toUrlPath($module). "/" . $this->toUrlPath($class)
]);
$this->info("Creating Controller $class");
$this->generateFile($class . "Controller", __DIR__ . '/../../resources/stubs/Crud/Controller.php.stub', $controllerPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Creating Services $class");
$this->generateFile($class . "Service", __DIR__ . '/../../resources/stubs/Crud/Service.php.stub', $servicePath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Running Optimize $class");
$this->call('optimize');
$this->info("Running Creating Migration $class");
// $this->call('make:migration', ['name' => "create_{$table}_table", '--create' => $table]);
$class = date("Y_m_d_His") . "_create_{$table}_table";
$this->generateFile($class, __DIR__ . '/../../resources/stubs/Crud/Migration.php.stub', $migrationPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Illuminate\Console\Command;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateServiceTaskJob extends Command
{
use GeneratorTrait, TableSpecHelper;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:servicetask:create {bpmn} {serviceTask} {topic} {replace?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$bpmn = $this->argument('bpmn');
if (!$bpmn) {
$bpmn = $this->ask("What is the model name?", $bpmn);
}
$replace = $this->argument('replace') == "true";
$serviceTask = $this->argument('serviceTask');
if (!$serviceTask) {
$serviceTask = $this->ask("What is Service Task name?", $serviceTask);
}
$topic = $this->argument('topic');
if (!$topic) {
$topic = $this->ask("What is topic name?", $topic);
}
$jobFolder = app_path() . "/Jobs" . "/" . $bpmn . "/";
$this->checkAndGenerateFolder($jobFolder);
$this->info("Creating Job $serviceTask");
$this->generateFile($serviceTask. "Job" , __DIR__ . '/../../resources/stubs/Bpmn/PROCESS/Job.php.stub', $jobFolder, [
"Class" => $serviceTask,
"bpmn" => $bpmn,
], $replace);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateUserTaskComand extends Command
{
use GeneratorTrait, TableSpecHelper;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:usertask:api {model} {table?} {module?} {replace?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command descriptUserTaskComandion';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$model = $this->argument('model');
$replace = $this->argument('replace') == "true";
if (!$this->argument("table")) {
$table = "";
while ($table == "") {
$table = $this->ask("What is the table name?", $this->toSnakeCase($model . "s") );
}
} else {
$table = $this->argument('table');
}
if (!$this->argument('module')) {
$module = "";
while ($module == "") {
$module = $this->ask("What is the module name ? ", $model);
}
} else {
$module = $this->argument('module');
}
$class = $model;
$controllerPath = app_path() . "/Http/Controllers/Api/Bpmn/" . $module . "/";
$this->checkAndGenerateFolder($controllerPath);
$servicePath = app_path() . "/Services/Bpmn/" . $module. "/";
$this->checkAndGenerateFolder($servicePath);
$modelPath = app_path() . "/Models/Bpmn/" . $module. "/" ;
$this->checkAndGenerateFolder($modelPath);
$migrationPath = base_path() . "/database/migrations/" ;
//
//
// $this->info("Creating Model $class");
// $modelStub = file_get_contents(__DIR__ . '/../../resources/stubs/Crud/Model.php.stub');
// $modelStub = str_replace("{Class}", $class, $modelStub);
// $modelStub = str_replace("{Module}", $module, $modelStub);
// $modelStub = str_replace("{table}", $table, $modelStub);
// file_put_contents(app_path() . "/Models/$class.php", $modelStub);
$this->info("Creating Model $class");
$this->generateFile($class, __DIR__ . '/../../resources/stubs/Bpmn/US/Model.php.stub', app_path() . "/Models/Bpmn/" .$module ."/" , [
"Class" => $class,
"Module" => $module,
"table" => $table,
"path" => "/api/" . $this->toUrlPath($module) . "/" . $this->toUrlPath($class)
], $replace);
$this->info("Creating Controller $class");
$this->generateFile($class. "Controller", __DIR__ . '/../../resources/stubs/Bpmn/US/Controller.php.stub', $controllerPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
$this->info("Creating Services $class");
$this->generateFile($class . "Service", __DIR__ . '/../../resources/stubs/Bpmn/US/Service.php.stub', $servicePath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
$this->info("Creating Migration $class");
$this->info("Running Optimize");
$this->call('optimize');
$this->info("Running Creating Migration $class");
// $this->call('make:migration', ['name' => "create_{$table}_table", '--create' => $table]);
$class = Carbon::now()->format("Y_m_d_His"). "_create_{$table}_table";
$this->generateFile($class , __DIR__ . '/../../resources/stubs/Bpmn/US/Migration.php.stub', $migrationPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
], $replace);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Illuminate\Console\Command;
class CreateView extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:make:view {tableName}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$tableName = $this->argument('tableName');
$class = $tableName;
// list file ON FOLDER
$controllerPath = base_path() . "/database/views/";
if (!is_dir($controllerPath)) {
mkdir($controllerPath, 0755, true);
}
$path = base_path() . "/database/views/" ;
$files = array_filter(scandir($path) , function ($file) {
return !in_array($file, ['.', '..']);
});
$number = 1;
if(count($files) == 0) {
$number = 1;
}else {
$lastFile = end($files);
$number = (int)explode("_", $lastFile)[0] + 1;
}
$filenme = base_path() . "/database/views/" . $number. "_" . $tableName . ".sql";
file_put_contents($filenme, "");
$this->info("File Created: " . $filenme);
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use Illuminate\Console\Command;
use Laravolt\Crud\Traits\GeneratorTrait;
class CreateViewApi extends Command
{
use GeneratorTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'laravolt:view:api {model}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$model = $this->argument('model');
$table = "";
while ($table == "") {
$table = $this->ask("What is the table name? {with schema name if needed}", strtolower($model));
}
$module = "";
while ($module == "") {
$module = $this->ask("What is the module name ? ", $model);
}
$this->info("Creating Module $module");
$createViewMigration = "";
while ($createViewMigration == "") {
$createViewMigration = $this->ask("Create migration [Y/n]? ", "Y");
}
$class = $model;
$controllerPath = app_path() . "/Http/Controllers/Api/Crud/" . $module. "/";
$this->checkAndGenerateFolder($controllerPath);
$servicePath = app_path() . "/Services/";
$this->checkAndGenerateFolder($controllerPath);
$this->checkAndGenerateFolder( app_path() . "/Models/$module/");
$this->info("Creating Model $class");
$this->generateFile($class, __DIR__ . '/../../resources/stubs/View/Model.php.stub', app_path() . "/Models/$module/", [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Creating Controller $class");
$this->generateFile($class . "Controller", __DIR__ . '/../../resources/stubs/View/Controller.php.stub', $controllerPath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Creating Services $class");
$this->generateFile($class . "Service", __DIR__ . '/../../resources/stubs/View/Service.php.stub', $servicePath, [
"Class" => $class,
"Module" => $module,
"table" => $table
]);
$this->info("Running Optimize $class");
$this->call('optimize');
if($createViewMigration == "Y") {
$this->info("Running Creating Migration $class");
$this->call('laravolt:make:view', ['tableName' => $table]);
}
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Commands;
use App\Jobs\IzinFindTribeleadJob;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Laravolt\Camunda\Http\ExternalTaskClient;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Crud\Helper\BpmnXmlHelper;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Schema\ColumnFactory;
use function Symfony\Component\String\s;
class PullProcessInstanceToExtend extends Command
{
use TableSpecHelper, BpmnXmlHelper;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'alurkerja:pullprocess:scafond {processDefinition} {replace?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
//
$replace = $this->argument('replace') == "true";
$this->info("Running Genearator With replace $replace " . ($replace ? "File akan di replace " : "File akan di skip"));
if ($replace) {
$resp = $this->ask("Lanjutkan ? (y/N)", $this->toSnakeCase("N"));
if ($resp == "N" || $resp == "n") {
return;
}
}
// $processName = $this->ask('Process Definition Key?');
$processName = $this->argument('processDefinition');
$specPath = resource_path($processName . '_spec.json');
$snakecase = $this->toSnakeCase($processName);
$xml_string = ProcessDefinitionClient::xml(key: $processName);
$doc = new \DOMDocument();
$doc->loadXML($xml_string);
$childNode = [];
foreach ($doc->childNodes as $node) {
array_push($childNode, $this->parseNode($node));
}
$data = $this->getNodeCollection($childNode);
$tasks = [];
foreach ($data as $index => $item) {
$attr = collect($item)->get('attributes');
$taskData = [
"type" => str_replace("bpmn:", "", $item['name']),
"name" => in_array('name', $attr) ? $attr['name'] : $attr['id']
];
if ($item['name'] == "bpmn:serviceTask") {
$taskData ['action_type'] = $attr['type'];
$taskData ['topic'] = $attr['topic'];
$this->info("Generating Service Task");
Artisan::call("laravolt:servicetask:create", [
'serviceTask' => ucfirst($taskData['name']),
'topic' => $attr['topic'],
'bpmn' => $processName,
'replace' => $replace
]);
$this->info("\nTambahkan text di bawha ini Pada `app/Providers/AppServiceProvider.php` \n" . "ExternalTaskClient::subscribe('" . $attr['topic'] . "', \App\Jobs\PengajuanIzinPegawai\\" . ucfirst($taskData['name'] . "Job::class") . "\n\n");
} else if ($item['name'] == "bpmn:userTask") {
$this->info("Generating US");
Artisan::call("laravolt:usertask:api", [
'table' => $this->toSnakeCase($processName) . "_" . $this->toSnakeCase($taskData['name']),
'model' => ucfirst($taskData['name']),
'module' => $processName,
'replace' => $replace
]);
} else if ($item['name'] == "bpmn:startEvent") {
$this->info("Generating Bpmn");
Artisan::call("laravolt:bpmn:api", [
'table' => $this->toSnakeCase($processName),
'model' => $processName,
'module' => $processName,
'replace' => $replace
]);
}
$tasks[$attr['id']] = $taskData;
}
$spec["tasks"] = $tasks;
// Write back to file
file_put_contents($specPath, json_encode($spec, JSON_PRETTY_PRINT));
return Command::SUCCESS;
}
}
<?php
namespace Laravolt\Crud\Contracts;
interface BpmnControllerContract
{
}
<?php
namespace Laravolt\Crud\Contracts;
interface CrudControllerContract
{
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface CrudUser
{
// public function permissionForView(CrudModel $model): PermissionLevel;
//
// public function permissionForCreate(CrudModel $model): PermissionLevel;
//
// public function permissionForUpdate(CrudModel $model): PermissionLevel;
//
// public function permissionForDelete(CrudModel $model): PermissionLevel;
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface CustomFieldContract
{
function getValue();
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface FeFormatSpecContract
{
function getValue();
function getName();
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface FieldSpecContract
{
function getValue();
}
<?php
namespace Laravolt\Crud\Contracts;
interface LazyExport
{
public function getLazyExportView(): string;
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface SelectionContract
{
function getValue();
}
<?php
namespace Laravolt\Crud\Contracts;
interface StoreRequestContract
{
public function all();
public function validated();
public function input(string $field, mixed $default = null);
public function except(string $field);
public function rules();
public function merge(array $input);
public function only(array $keys);
}
<?php
namespace Laravolt\Crud\Contracts;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
/**
* @property \Illuminate\Support\Collection $roles
* @property string $email
*/
interface TableValueContract
{
function getValue();
}
<?php
namespace Laravolt\Crud\Contracts;
interface ToExcel
{
public function toExcel(): array;
}
<?php
namespace Laravolt\Crud\Contracts;
interface UpdateRequestContract
{
public function all();
public function validated();
public function input(string $field);
public function except(array $except);
public function rules();
}
<?php
namespace Laravolt\Crud\Contracts;
interface UsertaskControllerContract
{
}
<?php
namespace Laravolt\Crud;
class CrudManager
{
private static ApiFormat $apiFormat = ApiFormat::Laravolt;
private static PrimaryKeyFormat $primaryKeyFormat = PrimaryKeyFormat::AUTO;
public static function setApiFormat(ApiFormat $format): void
{
self::$apiFormat = $format;
ApiCrudController::setFormat($format);
}
public static function getApiFormat(): ApiFormat
{
return self::$apiFormat;
}
/**
* @return \Laravolt\Crud\PrimaryKeyFormat
*/
public static function getPrimaryKeyFormat(): PrimaryKeyFormat
{
return self::$primaryKeyFormat;
}
/**
* @param \Laravolt\Crud\PrimaryKeyFormat $primaryKeyFormat
*/
public static function setPrimaryKeyFormat(PrimaryKeyFormat $primaryKeyFormat): void
{
self::$primaryKeyFormat = $primaryKeyFormat;
}
}
<?php
namespace Laravolt\Crud;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Column;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Laravolt\Crud\Contracts\CrudUser;
use Laravolt\Crud\Contracts\CustomFieldContract;
use Laravolt\Crud\Contracts\FieldSpecContract;
use Laravolt\Crud\Enum\AutoMode;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Schema\ColumnFactory;
use Laravolt\Crud\Spec\BaselanguageSpec;
use Laravolt\Crud\Spec\BaseSpec;
use Laravolt\Crud\Traits\AutoFilter;
use Laravolt\Crud\Traits\AutoSearch;
use Laravolt\Crud\Traits\AutoSort;
use ReflectionException;
use Spatie\MediaLibrary\HasMedia;
use Wildside\Userstamps\Userstamps;
/**
* @property int|string $created_by
* @property int|string $updated_by
* @property int|string $deleted_by
*/
class CrudModel extends Model
{
use AutoSort;
use AutoFilter;
use AutoSearch;
// use Userstamps;
/**
* @var string
*/
protected string $ownerKey = 'created_by';
protected $guarded = [];
protected $moduleName = '';
protected bool $isBpmn = false;
protected bool $isUserTask = false;
protected AutoMode $sortMode = AutoMode::WHITELIST;
protected AutoMode $filterMode = AutoMode::WHITELIST;
protected AutoMode $searchMode = AutoMode::WHITELIST;
protected $ignoreMediaDeletion = true;
protected array $searchableColumns = [];
protected array $filterableColumns = [];
protected array $sortableColumns = [];
protected array $exportableColumns = [];
protected array $mediableColumns = [];
protected array $nextcloudableColumn = [];
protected array $allowedScopes = [];
protected array $requiredFilters = [];
protected array $hiddenOnList = [];
protected array $hiddenOnCreate = [];
protected array $hiddenOnEdit = [];
protected array $hiddenOnDetail = [];
public function getHiddenOnList(): array
{
$default = ["created_at", "updated_at", "deleted_at", "created_by", "updated_by", "deleted_by"];
return array_merge($this->hiddenOnList ?: [], $default);
}
public function getHiddenOnCreate(): array
{
$default = ["created_at", "updated_at", "deleted_at", "created_by", "updated_by", "deleted_by"];
return array_merge($this->hiddenOnCreate ?: [], $default);
}
public function getHiddenOnEdit(): array
{
$default = ["created_at", "updated_at", "deleted_at", "created_by", "updated_by", "deleted_by"];
return array_merge($this->hiddenOnEdit ?: [], $default);
}
public function getHiddenOnDetail(): array
{
$default = [];
return array_merge($this->hiddenOnDetail ?: [], $default);
}
protected bool $canCreate = true, $canEdit = true, $canDelete = true, $canDetail = true, $canBulk = false;
protected string $path = '';
protected array $orderColumns = [];
protected array $listOrderColumns = [];
protected array $createOrderColumns = [];
protected array $editOrderColumns = [];
protected static function booted()
{
if (!in_array(HasMedia::class, class_implements(static::class))) {
static::resolveRelationUsing('media', function (CrudModel $model) {
return $model->morphMany(config('media-library.media_model'), 'model', 'model_type', 'model_id');
});
}
static::resolveRelationUsing('nextcloudMedia', function (CrudModel $model) {
return $model->morphMany(config('media-library.media_model'), 'model', 'model_type', 'model_id');
});
// static::created(static function (self $model) {
// DB::table("log.activity_logs")->insert([
// "type" => "create",
// "class_name" => get_class($model),
// "new_value" => $model->toJson(),
// "status" => "success",
// "created_at" => Carbon::now(),
// "created_by" => $model->created_by,
// ]);
// });
// static::updated(static function (self $model) {
// DB::table("log.activity_logs")->insert([
// "type" => "update",
// "class_name" => get_class($model),
// "new_value" => $model->toJson(),
// "old_value" => json_encode($model->getOriginal()),
// "status" => "success",
// "created_at" => Carbon::now(),
// "created_by" => $model->updated_by,
// ]);
// });
// static::deleted(static function (self $model) {
// DB::table("log.activity_logs")->insert([
// "type" => "delete",
// "class_name" => get_class($model),
// "old_value" => $model->toJson(),
// "status" => "success",
// "created_at" => Carbon::now(),
// "created_by" => isset($model->deleted_by) ? $model->deleted_by : null,
// ]);
// });
}
public function scopeFilterByPermission(Builder $query, CrudUser|Model|null $user, PermissionLevel $permission)
{
switch ($permission) {
case PermissionLevel::MINE:
/** @phpstan-ignore-next-line */
$query->mine($user);
break;
case PermissionLevel::OWNED:
/** @phpstan-ignore-next-line */
$query->owned($user);
break;
case PermissionLevel::NOTHING:
// do nothing
break;
case PermissionLevel::GUEST:
/** @phpstan-ignore-next-line */
$query->guest();
break;
case PermissionLevel::CUSTOM:
/** @phpstan-ignore-next-line */
$query->custom($user);
break;
case PermissionLevel::ALL:
// do nothing
}
}
public function scopeMine(Builder $query, CrudUser|Model $user)
{
$query->where($this->ownerKey, $user->getKey());
}
public function scopeCustom(Builder $query, CrudUser|Model $user)
{
$query->where($this->ownerKey, $user->getKey());
}
public function scopeGuest(Builder $query)
{
// $query->where($this->ownerKey, $user->getKey());
}
public function scopeOwned(Builder $query, CrudUser|Model $user)
{
$query->whereIn($this->ownerKey, [$user->getKey()]);
}
public function isMine(CrudUser|Model $user)
{
return $this->getAttribute($this->ownerKey) === $user->getKey();
}
public function isOwned(CrudUser|Model $user)
{
return $this->getAttribute($this->ownerKey) === $user->getKey();
}
public function isIgnoreMediaDeletion()
{
return $this->ignoreMediaDeletion;
}
/**
* @return array
*/
public function labelMapping()
{
return [];
}
public function getSearchableColumns(): array
{
if ($this->searchMode == AutoMode::BLACKLIST) {
$available = $this->getFieldWithType($this->searchableColumns);
$available = collect($available)->whereIn('type', ['integer', 'textarea', 'text', "number", "datetime-local", "date"])->toArray();
$available = array_keys($available);
return $available;
}
return $this->searchableColumns;
}
public function setSearchableColumns(array $searchable)
{
return $this->searchableColumns = $searchable;
}
public function getFilterableColumns(): array
{
if ($this->filterMode == AutoMode::BLACKLIST) {
$available = $this->getFieldWithType($this->filterableColumns);
$available = collect($available)->whereIn('type', ['integer', 'textarea', 'text', "number", "datetime-local", "date"])->toArray();
$available = array_keys($available);
return $available;
}
return $this->filterableColumns;
}
public function setFilterableColumns(array $filterable)
{
return $this->filterableColumns = $filterable;
}
public function getRequiredFilters(): array
{
return $this->requiredFilters;
}
public function getModuleName(): string
{
return $this->moduleName;
}
public function getSortableColumns(): array
{
if ($this->sortMode == AutoMode::BLACKLIST) {
$available = $this->getFieldWithType($this->sortableColumns);
$available = collect($available)->whereIn('type', ['integer', 'textarea', 'text', "number", "datetime-local", "date"])->toArray();
$available = array_keys($available);
return $available;
}
return $this->sortableColumns;
}
public function getExportableColumns(): array
{
return $this->exportableColumns;
}
public function getMediableColumns(): array
{
return $this->mediableColumns;
}
public function getNexcloudableColumns(): array
{
return $this->nextcloudableColumn;
}
public function getOwnerKey(): string
{
return $this->ownerKey;
}
public function getOwnerId(): mixed
{
return $this->{$this->ownerKey};
}
public function getAllowedScopes(): array
{
return $this->allowedScopes;
}
public function getFeFormatMapping(): array
{
return [];
}
public function defaultPayload(FormRequest|Request $request): array
{
$userPayload = [];
$userstamping = $this?->userstamping ?? true;
if ($request->has($this->getOwnerKey())) {
$userPayload[$this->getOwnerKey()] = $request->input($this->getOwnerKey());
}
// dont added payload creator_id / created_by if $userstamping false
if (!$userstamping) {
return $userPayload;
}
return $userPayload + [$this->getOwnerKey() => $request->user()?->id];
}
public function getDbFields($except = [])
{
$collumns = Cache::remember("crud_model_{$this->getTable()}_columns", 60, function () use ($except) {
$schemaManager = DB::getDoctrineSchemaManager();
try {
$columns = $schemaManager->listTableColumns($this->getTable());
} catch (Exception $e) {
$columns = [];
report($e);
}
return $columns;
});
// TODO: Cache this should be cached in production
return $collumns;
}
public function getFieldWithType($except)
{
$collumns = $this->getDbFields($except);
$collumns = collect($collumns)
->mapWithKeys(
function (Column $column) {
return [strtolower($column->getName()) => [
"type" => ColumnFactory::make($column)->inputType()
]];
}
)
->except($except)
->toArray();
return $collumns;
}
public function addRuleExceptField()
{
return [];
}
public function rules(Request $request): array
{
// @TODO how to skip column?
$except = ['id', 'created_at', 'updated_at', 'deleted_at', 'created_by', 'updated_by', 'deleted_by'];
$except = array_merge($except, $this->addRuleExceptField());
if (!in_array($request->route()?->getActionMethod(), ['store', 'start'])) {
$except[] = $this->getOwnerKey();
}
if ($this->getNexcloudableColumns()) {
$except = array_merge($except, $this->getNexcloudableColumns());
}
/** @var \Doctrine\DBAL\Schema\AbstractSchemaManager $schemaManager */
$collumns = $this->getDbFields($except);
$collumns = collect($collumns)
->mapWithKeys(
function (Column $column) {
return [strtolower($column->getName()) => ColumnFactory::make($column)->rules()];
}
)
->except($except)
->toArray();
return $collumns;
}
public function toArray()
{
$data = parent::toArray();
if ($this->relationLoaded('media')) {
foreach ($this->getMediableColumns() as $column) {
$data[$column] = $this->media->where('collection_name', $column)->toArray();
}
unset($data['media']);
}
if ($this->relationLoaded('nextcloudMedia')) {
foreach ($this->getNexcloudableColumns() as $column) {
$data[$column] = $this->nextcloudMedia->where('collection_name', $column)->toArray();
}
}
return $data;
}
public function permissionForView(CrudUser|null $user): PermissionLevel
{
return $user ? PermissionLevel::ALL : PermissionLevel::GUEST;
}
public function permissionForCreate(CrudUser|null $user): PermissionLevel
{
return $user ? PermissionLevel::MINE : PermissionLevel::ALL;
}
public function permissionForUpdate(CrudUser|null $user): PermissionLevel
{
return $user ? PermissionLevel::MINE : PermissionLevel::ALL;
}
public function permissionForDelete(CrudUser|null $user): PermissionLevel
{
return $user ? PermissionLevel::MINE : PermissionLevel::ALL;
}
/**
* @return FieldSpecContract[]
*/
public function tableValueMapping(): array
{
return [];
}
/**
* @return FieldSpecContract[]
*/
public function addAdditionalFieldSpec(): array
{
return [];
}
/**
* @return BaseCustomFieldAtribute[]
*/
public function setCustomFieldAtribute(): array
{
return [];
}
function getUserTask(): array
{
return [];
}
function parseModuleName()
{
$module = collect(explode("_", $this->table))->map(function ($text) {
return ucfirst($text);
})->join(" ");
return $module;
}
function getLanguangeOverride()
{
$defaultclang = config("alurkerjalang.default") ?? "ID";
return BaselanguageSpec::loadFromArray(config("alurkerjalang.$defaultclang"));
}
public function getOverrides(): array
{
$selection = $this->mapSelection();
$addionalFieldSpec = $this->mapAdditional($this->addAdditionalFieldSpec());
$tableValueMapping = $this->mapValueMapping($this->tableValueMapping());
$customAtribute = $this->mapCustomFieldAtribute($this->setCustomFieldAtribute());
$feMapping = $this->mapFeFormat($this->getFeFormatMapping());
$override = [
'is_hidden_in_list' => $this->getHiddenOnList(),
'is_hidden_in_edit' => $this->getHiddenOnEdit(),
'is_hidden_in_create' => $this->getHiddenOnCreate(),
'is_hidden_in_detail' => $this->getHiddenOnDetail(),
'selection_options' => $selection->toArray(),
'additional_field_spec' => $addionalFieldSpec->toArray(),
'table_value_mapping' => $tableValueMapping->toArray(),
'custom_field_atribute' => $customAtribute->toArray(),
'can_create' => $this->canCreate,
'can_edit' => $this->canEdit,
'can_delete' => $this->canDelete,
'can_bulk' => $this->canBulk,
'can_detail' => $this->canDetail,
'is_bpmn' => $this->isBpmn,
'is_usertask' => $this->isUserTask,
'filterable' => $this->getFilterableColumns(),
'order_columns' => $this->orderColumns,
'lang' => $this->getLanguangeOverride(),
'module_name' => $this->parseModuleName(),
'list_order_columns' => $this->listOrderColumns,
'create_order_columns' => $this->createOrderColumns,
'edit_order_columns' => $this->editOrderColumns,
'fe_format_mapping' => $feMapping->toArray(),
'label_mapping' => $this->labelMapping()
];
if ($this->isBpmn) {
$override['usertask_mapping'] = $this->getUserTask();
}
return $override;
}
/**
* @param \Illuminate\Support\Collection $selection
* @return \Illuminate\Support\Collection
*/
public function mapSelection(): \Illuminate\Support\Collection
{
$selection = collect(get_class_methods($this));
$selection = $selection->filter(function ($fn) {
return preg_match("/^get.*.Selection$/", $fn);
});
return $selection->mapWithKeys(function ($fn, $index) {
$field = str_replace("get", "", $fn);
$field = str_replace("Selection", "", $field);
$field = strtolower($field);
if (preg_match("/^get.*.Selection$/", $fn)) {
$preFunction = call_user_func([$this, $fn], []);
if (gettype($preFunction) == 'object' && is_subclass_of($preFunction, 'Laravolt\Crud\Contracts\SelectionContract')) {
return [$field => call_user_func([$preFunction, 'getValue'], [])];
} else {
throw new \ReflectionException("Must Implement Selection Contract");
}
}
return [$field => []];
});
}
public function mapAdditional(array $selection): \Illuminate\Support\Collection
{
$selection = collect($selection);
return $selection->mapWithKeys(function ($fn, $index) {
if (gettype($fn) == 'object' && is_subclass_of($fn, 'Laravolt\Crud\Contracts\FieldSpecContract')) {
$data = call_user_func([$fn, 'getValue'], []);
return [$data['name'] => $data];
} else {
throw new \ReflectionException("Must Implement Selection Contract");
}
});
}
public function mapFeFormat(array $selection): \Illuminate\Support\Collection
{
$selection = collect($selection);
return $selection->mapWithKeys(function ($fn, $index) {
if (gettype($fn) == 'object' && is_subclass_of($fn, 'Laravolt\Crud\Contracts\FeFormatSpecContract')) {
$data = call_user_func([$fn, 'getValue'], []);
return [$data['name'] => $data];
} else {
throw new \ReflectionException("Must Implement Selection Contract");
}
});
}
public function mapValueMapping(array $selection): \Illuminate\Support\Collection
{
$selection = collect($selection);
return $selection->mapWithKeys(function ($fn, $index) {
if (gettype($fn) == 'object' && is_subclass_of($fn, 'Laravolt\Crud\Contracts\TableValueContract')) {
$data = call_user_func([$fn, 'getValue'], []);
return [$data['name'] => $data];
} else {
throw new \ReflectionException("Must Implement Selection Contract");
}
});
}
public function mapCustomFieldAtribute(array $selection): \Illuminate\Support\Collection
{
$selection = collect($selection);
return $selection->mapWithKeys(function ($fn, $index) {
if (gettype($fn) == 'object' && is_subclass_of($fn, 'Laravolt\Crud\Contracts\CustomFieldContract')) {
$data = call_user_func([$fn, 'getValue'], []);
return [$data['name'] => $data];
} else {
throw new ReflectionException("Must Implement Selection Contract");
}
});
}
public function getPath(): string
{
return $this->path;
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Contracts\UpdateRequestContract;
class CrudRequest extends FormRequest implements StoreRequestContract, UpdateRequestContract
{
protected function prepareForValidation()
{
$model = request()?->route()?->getController()->model();
if (in_array(request()?->route()?->getActionMethod(), ['store', 'start', 'bulk'])) {
$this->merge($model->defaultPayload($this));
request()->request->add($model->defaultPayload($this));
}
}
public function rules()
{
/** @var \Laravolt\Crud\CrudModel $model */
$model = request()?->route()?->getController()->model();
if (!$model) {
return [];
}
return $model->rules($this);
}
protected function failedValidation(Validator $validator)
{
if (CrudManager::getApiFormat() === ApiFormat::AlurKerja) {
throw new \Laravolt\Crud\Response\AlurKerja\ValidationException($validator);
}
parent::failedValidation($validator);
}
}
<?php
namespace Laravolt\Crud;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Doctrine\DBAL\Schema\Column;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\SupportStr;
use Laravolt\Crud\Contracts\CrudUser;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Contracts\UpdateRequestContract;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Schema\ColumnFactory;
use Laravolt\Crud\Sys\ActivityLog\AkActivityLog;
use Laravolt\Crud\Traits\CanHandleMedia;
class CrudService
{
use AuthorizesRequests;
use CanHandleMedia;
use TableSpecHelper;
/**
* @var \Laravolt\Crud\CrudModel
*/
protected CrudModel $model;
/**
* @var array<int, string>
*/
protected array $autoLoadRelations = [];
protected ?Authenticatable $user;
public function __construct(CrudModel $model, CrudUser $user = null)
{
$this->model = $model;
$this->autoLoadRelations = $this->getAutoLoadRelations();
$this->user = $user ?? auth()->user();
}
public function appendQuery($query)
{
return $query;
}
/**
* @throws \Throwable
*/
public function get(Request $request): LengthAwarePaginator
{
$filter = $request->input('filter', []);
if (is_array($filter)) {
request()?->merge(['filter' => $this->validateFilters($filter ?: [])]);
}
$query = $this->model
->newQuery()
->with($this->autoLoadRelations)
->filterByPermission(
$this->user,
$this->user ? $this->model->permissionForView($this->user) : PermissionLevel::GUEST
);
$query = $this->searchQuery($query, $request);
$query = $this->filterQuery($query, $request);
$query = $this->sortQuery($query, $request);
$query = $this->appendQuery($query);
$limit = min($request->input('limit') === 'undefined' ? 100 : $request->input('limit'), 100);
$page = ($request->input('page') === 'undefined' ? 0 : $request->input('page')) + 1;
if ($request->show_all) {
$paged = $query->get();
$paged = new LengthAwarePaginator($paged, $paged->count(), $paged->count() ? : 100, 1);
} else {
$paged = $query->paginate(
$limit,
['*'],
'page',
$page
);
}
$selection = $this->model->mapSelection();
$paged = $paged->through(function ($data, $key) use ($selection) {
foreach ($selection as $index => $value) {
$tempData = $data->toArray();
if (array_key_exists($index, $tempData) && ($value['type'] == "INPUT_SELECT" || $value['type'] == "INPUT_RADIO")) {
$opsi = collect($value['options']);
$found = $opsi->filter(function ($filtered) use ($value, $data, $index) {
return $filtered[$value['option_key']] == $data[$index];
});
$data[$index . '_selection'] = $found->first();
}
}
return $data;
});
unset($query, $filter, $selection);
return $this->afterPaginate($paged);
}
protected function searchQuery(Builder $query, Request $request): Builder
{
$query->autoSearch($request->input('search'));
return $query;
}
protected function filterQuery(Builder $query, Request $request): Builder
{
$query->autoFilter();
$filterByScopes = (array)$request->input('filter.scope');
foreach ($filterByScopes as $key => $scope) {
if (is_int($key)) {
if (in_array("scope.$scope", $this->model->getFilterableColumns(), true)) {
$query->{$scope}();
}
} elseif (is_string($key) && is_array($scope)) {
$params = $scope;
$scope = $key;
$query->{$scope}($params);
}
}
return $query;
}
protected function sortQuery(Builder $query, Request $request): Builder
{
$sortByColumn = $request->input('sort');
if (in_array($sortByColumn, $this->model->getSortableColumns(), true)) {
$sortBy = $request->input('sort');
if (Str::of($sortBy)->startsWith('scope.')) {
$scope = (string)Str::of($sortBy)->after('scope.');
$query->{$scope}();
} else {
$query->autoSort($request->query(), 'sort', 'direction');
}
}
return $query;
}
public function getByScope(Request $request, string $scope)
{
$filter = $request->input('filter', []);
request()?->merge(['filter' => $this->validateFilters($filter ?: [])]);
$query = $this->model
->newQuery()
->with($this->autoLoadRelations)
->filterByPermission(
$this->user,
$this->user ? $this->model->permissionForView($this->user) : PermissionLevel::GUEST
)
->autoSearch($request->input('search'))
->autoSort()
->autoFilter()
->{$scope}();
$data = $query->paginate(min($request->input('limit'), 100), ['*'], 'page', $request->input('page', 0) + 1);
$hookMethod = 'afterScope' . Str::title($scope);
if (method_exists($this, $hookMethod)) {
$data = $this->{$hookMethod}($data);
}
return $data;
}
public function all(Request $request, array $columns = ['*']): \Illuminate\Database\Eloquent\Collection
{
$filter = $request->input('filter', []);
request()?->merge(['filter' => $this->validateFilters($filter ?: [])]);
if (method_exists($this->model, 'tableForExport')) {
$this->model->setTable($this->model->tableForExport());
}
$query = $this->model
->newQuery()
->with($this->autoLoadRelations)
->filterByPermission(
$this->user,
$this->user ? $this->model->permissionForView($this->user) : PermissionLevel::GUEST
)
->autoSearch($request->input('search'))
->autoFilter();
$filterByScopes = (array)$request->input('filter.scope');
foreach ($filterByScopes as $key => $scope) {
if (is_int($key)) {
if (in_array("scope.$scope", $this->model->getFilterableColumns(), true)) {
$query->{$scope}();
}
} elseif (is_string($key) && is_array($scope)) {
$params = $scope;
$scope = $key;
$query->{$scope}($params);
}
}
$sortByColumn = $request->input('sort');
if (in_array($sortByColumn, $this->model->getSortableColumns(), true)) {
$sortBy = $request->input('sort');
if (Str::of($sortBy)->startsWith('scope.')) {
$scope = (string)Str::of($sortBy)->after('scope.');
$query->{$scope}();
} else {
$query->autoSort($request->query(), 'sort', 'direction');
}
}
return $query->get($columns);
}
public function find(mixed $id): CrudModel | Collection
{
return $this->afterFind(
$this->model
->newQuery()
->with($this->autoLoadRelations)
->findOrFail($id)
);
}
public function beforeCreateHook(StoreRequestContract|FormRequest $requestContract)
{
return $requestContract;
}
public function afterCreateHook(CrudModel $crudModel)
{
return $crudModel;
}
public function beforeUpdateHook(mixed $id, UpdateRequestContract|FormRequest $requestContract)
{
return $requestContract;
}
public function afterUpdateHook(mixed $id, CrudModel $crudModel)
{
return $crudModel;
}
public function prepareCreateData($request)
{
$request = $this->beforeCreateHook($request);
$validated = $request->all();
return $validated;
}
public function create(StoreRequestContract|FormRequest $request): CrudModel
{
$validated = $this->prepareCreateData($request);
$validated = array_merge($validated, $this->transformCreateRequest($request));
foreach ($this->model->getMediableColumns() as $column) {
if (key_exists($column, $validated)) {
unset($validated[$column]);
}
}
$model = $this->model->newQuery()->create($validated);
$this->processMediaable($request, $model);
// $this->processNexcloudable($request, $model);
$model->load($this->autoLoadRelations);
$model->refresh();
AkActivityLog::createCrudlog(
$model,
"CREATE_DATA"
);
return $this->afterCreateHook($model);
}
public function update(mixed $id, UpdateRequestContract|FormRequest $request): CrudModel
{
$request = $this->beforeUpdateHook($id, $request);
$model = $this->model
->newQuery()
->findOrFail($id);
// $this->processNexcloudable($request, $model, 'UPDATE');
$this->processMediaable($request, $model, 'UPDATE');
$except = array_merge($this->model->getMediableColumns(), $this->model->getNexcloudableColumns());
$validated = $request->all();
$model->update(collect($validated)->except($except)->toArray());
$model->load($this->autoLoadRelations);
AkActivityLog::createCrudlog(
$model,
"UPDATE_DATA"
);
return $this->afterUpdateHook($id, $model);
}
public function delete(mixed $model): ?bool
{
if ($model instanceof CrudModel) {
return $model->delete();
}
AkActivityLog::createCrudlog(
$model,
"DELETE_DATA"
);
return $this->model
->newQuery()
->findOrFail($model)
->delete();
}
public function spec(): Collection
{
$table = $this->model->getTable();
$overrides = $this->model->getOverrides();
$path = $this->model->getPath();
return collect($this->getTableSpec($table, $overrides, $path));
}
public function rules(): Collection
{
return collect($this->model->rules(request()));
}
/**
* @return array<int, string>
*/
private function getAutoLoadRelations(): array
{
$reflector = new \ReflectionClass($this->model);
$relationships = collect($reflector->getMethods())
->filter(
function (\ReflectionMethod $method) {
/** @var \ReflectionNamedType $returnType */
$returnType = $method->getReturnType();
return $returnType !== null && method_exists($returnType, "getName")
&& in_array(
$returnType->getName(),
[
BelongsTo::class,
HasOne::class,
HasMany::class,
HasManyThrough::class,
HasOneThrough::class,
]
);
}
)
->pluck('name');
$mediableColumns = $this->model->getMediableColumns();
if (!empty($mediableColumns)) {
$relationships->push('media');
}
$nextcloudColumn = $this->model->getNexcloudableColumns();
if (!empty($nextcloudColumn)) {
$relationships->push('nextcloudMedia');
}
//
// if ($this->model instanceof BpmnModel) {
// $relationships->push('logs');
// }
return $relationships->toArray();
}
/**
* @throws \Throwable
*/
public function validateFilters(array $filters): array
{
if (empty($filters)) {
return [];
}
// Validate $requiredFilters
$requiredFilters = $this->model->getRequiredFilters();
foreach ($requiredFilters as $requiredFilter) {
if (!isset($filters[$requiredFilter])) {
throw new \InvalidArgumentException(sprintf('Required filters: %s', $requiredFilter));
}
}
// Validate Date/Datetime filter
foreach ($filters as $column => $filter) {
if ($this->model->hasCast($column, 'datetime')) {
try {
$filters[$column] = Carbon::parse($filter)->toDateString();
} catch (InvalidFormatException $e) {
throw new \InvalidArgumentException(sprintf('Invalid datetime format: %s', $filter));
}
}
}
// Validate dotted filter, e.g. filter[user.id]
// Make sure that "user" is a valid relation
foreach ($filters as $column => $filter) {
if (Str::contains($column, '.')) {
$relationName = Str::beforeLast($column, '.');
if (!$this->model->newQuery()->getRelation($relationName) instanceof Relation) {
throw new \Exception(
sprintf('%s has no relation named "%s"', get_class($this->model), $relationName)
);
}
}
}
return $filters;
}
protected function afterPaginate(LengthAwarePaginator $data): LengthAwarePaginator
{
return $data;
}
protected function afterFind(CrudModel $model): CrudModel
{
return $model;
}
public function transformCreateRequest(StoreRequestContract|Request $request): array
{
return [];
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Laravolt\Crud\Commands\CreateBpmnApi;
use Laravolt\Crud\Commands\CreateControllerWithService;
use Laravolt\Crud\Commands\CreateCrudApi;
use Laravolt\Crud\Commands\CreateServiceTaskJob;
use Laravolt\Crud\Commands\CreateUserTaskComand;
use Laravolt\Crud\Commands\CreateView;
use Laravolt\Crud\Commands\CreateViewApi;
use Laravolt\Crud\Commands\PullProcessInstanceToExtend;
use Laravolt\Crud\Commands\PullServiceTaskToExtend;
use Laravolt\Crud\Contracts\CrudUser;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\RouteDiscovery\HandleCrudRoutes;
use ParseTableToSpec;
enum DataType
{
case UUID;
case INTEGER;
}
class CrudServiceProvider extends ServiceProvider
{
public function boot(): void
{
$default = config()->get('route-discovery.pending_route_transformers');
if ($default) {
if (!in_array(HandleCrudRoutes::class, $default, true)) {
$default[] = HandleCrudRoutes::class;
config()->set('route-discovery.pending_route_transformers', $default);
}
}
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'crud');
Blueprint::macro('userstamps', function (array $only = [], DataType|string $created_by_type = DataType::INTEGER) {
/** @var Blueprint $blueprint */
$blueprint = $this;
if (empty($only)) {
$only = ['created_by', 'updated_by', 'deleted_by'];
}
foreach ($only as $column) {
switch ($created_by_type) {
case DataType::UUID :
$blueprint->uuid($column)->nullable();
break;
default :
$blueprint->unsignedBigInteger($column)->nullable();
break;
}
}
});
Blueprint::macro('dropUserstamps', function (array $only = []) {
/** @var Blueprint $blueprint */
$blueprint = $this;
if (empty($only)) {
$only = ['created_by', 'updated_by', 'deleted_by'];
}
foreach ($only as $column) {
if (Schema::hasColumn($blueprint->getTable(), $column)) //check the column
{
$blueprint->dropColumn($column);
}
}
});
Gate::define('viewAny', function (?CrudUser $user, CrudModel $model) {
//TODO butuh mekanisme yang lebih secure
if (is_null($user) && request()->hasHeader('x-widget')) {
return true;
}
$currentMiddleware = \request()?->route()?->gatherMiddleware() ?? [];
$guestAllowed = array_search('allow_guest', $currentMiddleware) > 0;
return !$guestAllowed ? ($model->permissionForView($user) !== PermissionLevel::NOTHING) : PermissionLevel::GUEST;
});
Gate::define('view', function (?CrudUser $user, CrudModel $model) {
//TODO butuh mekanisme yang lebih secure
if (is_null($user) && request()->hasHeader('x-widget')) {
return true;
}
return match ($model->permissionForView($user)) {
PermissionLevel::NOTHING => false,
PermissionLevel::MINE => $model->isMine($user),
PermissionLevel::OWNED => $model->isOwned($user),
default => true,
};
});
Gate::define('create', function (CrudUser $user, CrudModel $model) {
//TODO butuh mekanisme yang lebih secure
if (is_null($user) && request()->hasHeader('x-widget')) {
return true;
}
return $model->permissionForCreate($user) !== PermissionLevel::NOTHING;
});
Gate::define('update', function (CrudUser $user, CrudModel $model) {
return match ($model->permissionForUpdate($user)) {
PermissionLevel::NOTHING => false,
PermissionLevel::MINE => $model->isMine($user),
PermissionLevel::OWNED => $model->isOwned($user),
default => true,
};
});
Gate::define('delete', function (CrudUser $user, CrudModel $model) {
return match ($model->permissionForDelete($user)) {
PermissionLevel::NOTHING => false,
PermissionLevel::MINE => $model->isMine($user),
PermissionLevel::OWNED => $model->isOwned($user),
default => true,
};
});
Gate::after(function ($user, $ability, $result, $arguments) {
if (!in_array($ability, ['viewAny', 'view', 'create', 'update', 'delete'])) {
foreach ($arguments as $model) {
if ($model instanceof CrudModel) {
$method = 'permissionFor' . ucfirst($ability);
if (!method_exists($model, $method)) {
continue;
}
$allowed = match ($model->$method($user)) {
PermissionLevel::ALL => true,
PermissionLevel::NOTHING => false,
PermissionLevel::MINE => $model->isMine($user),
PermissionLevel::OWNED => $model->isOwned($user),
default => null,
};
if ($allowed !== null) {
return $allowed;
}
}
}
}
});
$default = config()->get('scribe.strategies.bodyParameters');
if ($default) {
if (!in_array(\Laravolt\Crud\Docs\Strategy\GetFromFormRequest::class, $default, true)) {
$default[] = \Laravolt\Crud\Docs\Strategy\GetFromFormRequest::class;
config()->set('scribe.strategies.bodyParameters', $default);
}
if (!in_array(\Laravolt\Crud\Docs\Strategy\GetFormFieldFromSpec::class, $default, true)) {
$default[] = \Laravolt\Crud\Docs\Strategy\GetFormFieldFromSpec::class;
config()->set('scribe.strategies.bodyParameters', $default);
}
}
/*
* use Illuminate\Support\Collection;
* use Illuminate\Pagination\LengthAwarePaginator;
*
* Paginate a standard Laravel Collection.
*
* @param int $perPage
* @param int $total
* @param int $page
* @param string $pageName
* @return array
*/
Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
$page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
return new LengthAwarePaginator(
$this->forPage($page, $perPage),
$total ?: $this->count(),
$perPage,
$page,
[
'path' => LengthAwarePaginator::resolveCurrentPath(),
'pageName' => $pageName,
]
);
});
$default = config()->get('scribe.strategies.metadata');
if ($default) {
if (!in_array(\Laravolt\Crud\Docs\Strategy\GetGroupByControllerName::class, $default, true)) {
$default[] = \Laravolt\Crud\Docs\Strategy\GetGroupByControllerName::class;
config()->set('scribe.strategies.metadata', $default);
}
}
$this->publishes([
__DIR__ . '/../config/alurkerjalang.php' => config_path('alurkerjalang.php'),
], 'config');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
public function register()
{
$this->commands([
CreateView::class,
CreateCrudApi::class,
CreateViewApi::class,
CreateBpmnApi::class,
CreateUserTaskComand::class,
CreateControllerWithService::class,
PullProcessInstanceToExtend::class,
CreateServiceTaskJob::class
]);
$this->mergeConfigFrom(
__DIR__ . '/../config/alurkerjalang.php', 'alurkerjalang'
);
parent::register(); // TODO: Change the autogenerated stub
}
}
<?php
namespace Laravolt\Crud\Docs\Strategy;
use Knuckles\Camel\Extraction\ExtractedEndpointData;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
use Laravolt\Crud\BaseController;
use Laravolt\Crud\BpmnModel;
use Laravolt\Crud\UserTaskModel;
use ReflectionMethod;
class GetFormFieldFromSpec extends Strategy
{
public function __invoke(ExtractedEndpointData $endpointData, array $routeRules = []): ?array
{
/**
* @var \Laravolt\Crud\BaseController;
*/
$controller = new ($endpointData->controller->name);
$method = $endpointData->httpMethods[0];
$result = [];
$mappingType = [
'text' => 'string',
'number' => 'integer',
'json' => 'object',
];
$excludes = [
'id',
'created',
'creator_id',
'updated',
'updater_id',
'is_deleted',
'deleted',
'deleter_id',
'process_instance_id',
'available_task',
];
if ($controller instanceof BaseController && $method === 'POST') {
$reflection = new ReflectionMethod($controller, 'service');
if ($reflection->isPublic()) {
/**
* @var \Laravolt\Crud\CrudService
*/
$service = $controller->service();
$spec = $service->spec();
$fields = $spec['fields'];
foreach ($fields as $field => $value) {
if (! in_array($field, $excludes)) {
$result[$field] = [
'name' => $value['name'],
'type' => $mappingType[$value['type']] ?? 'string',
'description' => $value['label'],
'required' => $value['required'],
];
}
}
$model = $controller->model();
if ($model instanceof BpmnModel || $model instanceof UserTaskModel) {
$variables = $model->getProcessVariables();
foreach ($variables as $variable) {
if (! array_key_exists($variable, $result)) {
$result[$variable] = [
'name' => $variable,
'type' => 'string',
'description' => 'Camunda variable',
'required' => true,
];
}
}
}
}
}
return $result;
}
}
<?php
namespace Laravolt\Crud\Docs\Strategy;
use Illuminate\Http\Request;
use Knuckles\Scribe\Extracting\Strategies\GetFromFormRequestBase;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\CrudRequest;
use ReflectionClass;
use ReflectionException;
use ReflectionFunctionAbstract;
use ReflectionUnionType;
class GetFromFormRequest extends GetFromFormRequestBase
{
protected string $customParameterDataMethodName = 'bodyParameters';
protected function isFormRequestMeantForThisStrategy(ReflectionClass $formRequestReflectionClass): bool
{
// Only use this FormRequest for body params if there's no "Query parameters" in the docblock
// Or there's a bodyParameters() method\
$formRequestDocBlock = $formRequestReflectionClass->getDocComment();
if (strpos(strtolower($formRequestDocBlock), "query parameters") !== false
|| $formRequestReflectionClass->hasMethod('queryParameters')) {
return false;
}
return true;
}
protected function getFormRequestReflectionClass(ReflectionFunctionAbstract $method): ?ReflectionClass
{
foreach ($method->getParameters() as $argument) {
$argType = $argument->getType();
if ($argType === null || $argType instanceof ReflectionUnionType) continue;
$argumentClassName = $argType->getName();
if($argumentClassName == "Laravolt\Crud\Contracts\StoreRequestContract" || $argumentClassName == "Laravolt\Crud\Contracts\UpdateRequestContract") {
$argumentClass = new CrudRequest();
$argumentClassName ="Laravolt\Crud\CrudRequest";
}
if (!class_exists($argumentClassName)) continue;
try {
$argumentClass = new ReflectionClass($argumentClassName);
} catch (ReflectionException $e) {
continue;
}
if (
(class_exists(LaravelFormRequest::class) && $argumentClass->isSubclassOf(LaravelFormRequest::class))
|| (class_exists(DingoFormRequest::class) && $argumentClass->isSubclassOf(DingoFormRequest::class))
|| (class_exists(CrudRequest::class) && $argumentClass->isSubclassOf(Request::class))
) {
return $argumentClass;
}
}
return null;
}
}
<?php
namespace Laravolt\Crud\Docs\Strategy;
use Knuckles\Camel\Extraction\ExtractedEndpointData;
use Knuckles\Scribe\Attributes\Authenticated;
use Knuckles\Scribe\Attributes\Endpoint;
use Knuckles\Scribe\Attributes\Group;
use Knuckles\Scribe\Attributes\Subgroup;
use Knuckles\Scribe\Attributes\Unauthenticated;
use Knuckles\Scribe\Extracting\ParamHelpers;
use Knuckles\Scribe\Extracting\Strategies\PhpAttributeStrategy;
use Knuckles\Scribe\Extracting\Strategies\Strategy;
/**
* @extends PhpAttributeStrategy<Group|Subgroup|Endpoint|Authenticated>
*/
class GetGroupByControllerName extends Strategy
{
public function __invoke(ExtractedEndpointData $endpointData, array $routeRules = []): array
{
$groupname = explode("/" , $endpointData->uri)[config("scribe.module_index") ?? 1] ;
$metadata = [
'groupName' => ucfirst($groupname),
'authenticated' => true
];
return $metadata;
}
}
<?php
namespace Laravolt\Crud\Enum;
enum AutoMode: int
{
case WHITELIST= 1;
case BLACKLIST= 2;
}
<?php
namespace Laravolt\Crud\Enum;
enum PermissionLevel: int
{
case NOTHING = 0;
case MINE = 1;
case OWNED = 2;
case ALL = 3;
case GUEST = -99;
case CUSTOM = -98;
}
<?php
namespace Laravolt\Crud\Exceptions;
class CrudException extends \Exception
{
}
<?php
namespace Laravolt\Crud\Helper;
trait BpmnXmlHelper
{
public function parseAtribute(\DOMNamedNodeMap $attributes)
{
$childNode = [];
foreach ($attributes as $index => $node) {
$data = [
"name" => $node->name,
"value" => $node->value,
];
if ($this->parseChildNode($node->childNodes)) {
$data["child"] = $this->parseChildNode($node->childNodes);
}
$childNode[$node->name] = $node->value;
}
return $childNode;
}
public function parseNode($node)
{
$nodeData = [];
$attr = $node->attributes ? $this->parseAtribute($node->attributes) : [];
if ($attr) {
$nodeData["name"] = $node->nodeName;
$nodeData["attributes"] = $attr;
}
$childNode = [];
if ($node->childNodes) {
foreach ($node->childNodes as $index => $node) {
$childNode[$index] = $this->parseNode($node);
}
}
if ($childNode)
$nodeData['child'] = $childNode;
return $nodeData;
}
public function parseChildNode($childNodes)
{
$childNode = [];
if ($childNodes) {
foreach ($childNodes as $index => $node) {
$childNode[$index] = $this->parseNode($node);
}
}
return $childNode;
}
/**
* @param array $childNode
* @return \Illuminate\Support\Collection
*/
public function getNodeCollection(array $childNode, $types = ['bpmn:userTask', 'bpmn:serviceTask', 'bpmn:startEvent']): \Illuminate\Support\Collection
{
$collected = collect($childNode);
$collected = collect($collected->first());
$collected = collect($collected->get("child"));
$data = collect($collected->where("name", "bpmn:process")->first());
$data = collect($data)->get("child");
$data = collect($data)->whereIn("name", $types);
return $data;
}
}
<?php
namespace Laravolt\Crud\Helper;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Column;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Laravolt\Crud\Schema\ColumnFactory;
use Laravolt\Crud\Spec\BaselanguageSpec;
trait TableSpecHelper
{
public function getTableSpec($table, array $overrides = [], string $path = '')
{
$cacheTtl = 30 * 60;
if (config("app.env") != 'production') {
$cacheTtl = 0;
}
return Cache::remember("spec_file_$table" . "_$path", $cacheTtl, function () use ($cacheTtl, $table, $overrides, $path) {
Log::info("Cache For spec_file_$table" . "_$path created for $cacheTtl");
$humanTable = $overrides["module_name"];
if ($path == '') {
$path = "/api/crud/" . $this->fromSnakeToUrlPath($table);
}
$isBpmn = $overrides['is_bpmn'] ?? false;
$isUserTask = $overrides['is_usertask'] ?? false;
$languageOverride = $overrides['lang'];
$humanize = $overrides;
$fieldAction = [
[
"label" => "Detail",
"action_label" => $languageOverride->getLang("detail_button_label", $humanTable),
"method" => "get",
"form_type" => "modal",
"path" => $path . "/{id}",
"icon" => "eye",
"type" => "primary",
],
[
"label" => "Edit",
"action_label" => $languageOverride->getLang("edit_button_label", $humanTable),
"method" => "put",
"form_type" => "modal",
"path" => $path . "/{id}",
"icon" => "edit",
"type" => "primary",
],
[
"label" => "Hapus",
"action_label" => $languageOverride->getLang("delete_button_label", $humanTable),
"method" => "delete",
"form_type" => "confirm_modal",
"confirm" => [
"title" => $languageOverride->getLang("delete_confirm_header", $humanTable),
"message" => $languageOverride->getLang("delete_confirm_message", $humanTable),
"confirm_text" => $languageOverride->getLang("delete_confirm_conirm_label", $humanTable),
"cancel_text" => $languageOverride->getLang("delete_confirm_cancel_label", $humanTable),
],
"path" => $path . "/{id}",
"icon" => "trash",
"type" => "danger",
],
];
if ($isBpmn) {
$fieldAction[] = [
"label" => "Start Process",
"action_label" => $languageOverride->getLang("start_process_button_label", $humanTable),
"method" => "post",
"form_type" => "confirm_modal",
"confirm" => [
"title" => $languageOverride->getLang("submit_task_confirm_header", $humanTable),
"message" => $languageOverride->getLang("submit_task_confirm_message", $humanTable),
"confirm_text" => $languageOverride->getLang("submit_task_confirm_conirm_label", $humanTable),
"cancel_text" => $languageOverride->getLang("submit_task_confirm_cancel_label", $humanTable),
],
"path" => $path . "/{id}/start",
"icon" => "play",
"type" => "danger",
];
}
if ($isUserTask) {
$fieldAction[] = [
"label" => "Submit Task",
"action_label" => $languageOverride->getLang("start_process_confirm_header", $humanTable),
"method" => "post",
"form_type" => "confirm_modal",
"confirm" => [
"title" => $languageOverride->getLang("start_process_confirm_header", $humanTable),
"message" => $languageOverride->getLang("start_process_confirm_message", $humanTable),
"confirm_text" => $languageOverride->getLang("start_process_confirm_conirm_label", $humanTable),
"cancel_text" => $languageOverride->getLang("start_process_confirm_cancel_label", $humanTable),
],
"path" => $path . "/task/{id}/submit",
"icon" => "play",
"type" => "danger",
];
}
$headerAction = [
[
"label" => "Tambah",
"action_label" => $languageOverride->getLang("create_button_label", $humanTable),
"method" => "post",
"form_type" => "new_page",
"path" => $path,
"icon" => "plus",
"type" => "primary",
],
];
if ($isBpmn) {
$headerAction[] = [
"label" => "Tambah",
"action_label" => $languageOverride->getLang("create_then_start_process_button_label", $humanTable),
"method" => "post",
"form_type" => "new_page",
"path" => $path . "/start",
"icon" => "plus",
"type" => "primary",
];
}
$table = strtolower($table);
$item = [
"show_as_menu" => true,
"name" => $table,
"is_bpmn" => $isBpmn,
"is_usertask" => $isUserTask,
"can_bulk" => $overrides["can_bulk"] ?? false,
"can_create" => $overrides["can_create"] ?? true,
"can_delete" => $overrides["can_delete"] ?? true,
"can_edit" => $overrides["can_edit"] ?? true,
"can_detail" => $overrides["can_detail"] ?? true,
"label" => ucfirst($table),
"base_url" => config("app.url"),
"path" => $path,
"description" => $languageOverride->getLang("field_list_description", $humanTable),
"header_action" => $headerAction,
"field_action" => $fieldAction,
"languages" => [
"pagination_info" => $languageOverride->getLang("pagination_info", '{page}', '{limit}', '{total}'),
"empty_data" => $languageOverride->getLang("empty_data_message", $humanTable),
"filter_title" => $languageOverride->getLang("filter_title"),
"filter_submit" => $languageOverride->getLang("filter_submit"),
"filter_reset" => $languageOverride->getLang("filter_reset"),
"filter_cancel" => $languageOverride->getLang("filter_cancel"),
]
];
if ($overrides['is_bpmn'] ?? false) {
$item['usertask_mapping'] = $overrides['usertask_mapping'];
}
$schemaManager = DB::getDoctrineSchemaManager();
try {
$relations = $schemaManager->listTableForeignKeys($table);
$columns = $schemaManager->listTableColumns($table);
} catch (Exception $e) {
$columns = [];
report($e);
}
// TODO: Cache this should be cached in production
$fields = collect($columns)
->mapWithKeys(
function (Column $column) {
return [strtolower($column->getName()) => ColumnFactory::make($column)->lowcodespec()];
}
)
->toArray();
if (array_key_exists("additional_field_spec", $overrides)) {
$fields = array_merge($fields, $overrides['additional_field_spec']);
}
//
// $fields = collect($fields)
// ->mapWithKeys(
// function ($field, $key) use ($overrides) {
// foreach ($overrides as $keyField => $ovrrideFiled) {
// if (in_array($key, $ovrrideFiled)) {
// $field[strtolower($keyField)] = true;
//
// } else if ($keyField == "selection_options") {
// if (in_array($key, array_keys($ovrrideFiled))) {
// $data = $ovrrideFiled[$key];
// $field['form_field_type'] = $data['type'];
// $field['select_options'] = $data;
// }
// } else if ($keyField == "table_value_mapping") {
// if (in_array($key, array_keys($ovrrideFiled))) {
// $field['table_value_mapping'] = $ovrrideFiled[$key];
// }
// }
// }
//
// return [$key => $field];
// }
// )
// ->toArray();
$relationsData = [];
if ($relations) {
$fks = [];
foreach ($relations as $key => $column) {
if ($fields[strtolower($column->getLocalColumns()[0])]) {
$fields[strtolower($column->getLocalColumns()[0])]['form_field_type'] = "INPUT_FOREIGN-SELECT";
$myPath = explode("/", $path);
array_pop($myPath);
$myPath = implode("/", $myPath);
$fields[strtolower($column->getLocalColumns()[0])]['select_options'] = [
"url" => $myPath . "/" . substr($this->fromSnakeToUrlPath(strtolower($column->getForeignTableName())), 0, -1) . "?show_all=true",
"table" => strtolower($column->getForeignTableName()),
"method" => "GET",
"option_key" => strtolower($column->getForeignColumns()[0]),
"option_label" => "name" // TODO: ini masih harus cari carnya biar dapet label / placeholder select nya
];
}
$relationsData += ([strtolower($column->getName()) => [
$column->getForeignTableName() => [
"type" => "belongsTo",
"from_key" => strtolower($column->getLocalColumns()[0]),
"destination_table" => strtolower($column->getForeignTableName()),
"destination_key" => strtolower($column->getForeignColumns()[0]),
],
]]);
}
}
$fields = collect($fields)
->mapWithKeys(
function ($field, $key) use ($overrides) {
foreach ($overrides as $keyField => $overrideFiled) {
if (is_array($overrideFiled)) {
if (in_array($key, $overrideFiled)) {
$field[strtolower($keyField)] = true;
} else if ($keyField == "selection_options") {
if (in_array($key, array_keys($overrideFiled))) {
$data = $overrideFiled[$key];
$field['form_field_type'] = $data['type'];
$field['select_options'] = $data;
}
} else if ($keyField == "label_mapping") {
if (in_array($key, array_keys($overrideFiled))) {
$data = $overrideFiled[$key];
$field['label'] = $data;
}
} else if ($keyField == "table_value_mapping") {
if (in_array($key, array_keys($overrideFiled))) {
$field['table_value_mapping'] = $overrideFiled[$key];
}
} else if ($keyField == "custom_field_atribute") {
if (in_array($key, array_keys($overrideFiled))) {
$data = $overrideFiled[$key];
$field['form_field_type'] = $data['type'];
$field['custom_field_atribute'] = $data;
}
} else if ($keyField == "fe_format_mapping") {
if (in_array($key, array_keys($overrideFiled))) {
$data = $overrideFiled[$key];
$field['format'] = $data['format'];
}
}
}
}
return [$key => $field];
}
)
->toArray();
if (array_key_exists("order_columns", $overrides)) {
if (count($overrides['order_columns']) > 0) {
$fields = $this->reOrderFields($fields, $overrides['order_columns']);
}
}
$fields = $this->orderingFields($fields, $overrides);
$item['fields'] = $fields;
$item['relations'] = $relationsData;
return $item;
});
}
public function toSnakeCase($processName)
{
return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $processName)), '_');
}
public function toWord($string)
{
$words = preg_replace('/(?<!\ )[A-Z]/', ' $0', $string);
return ucwords($words);
}
public function fromSnakeToUrlPath($processName)
{
return str_replace("_", "-", $processName);
}
public function toUrlPath($processName)
{
return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '-$0', $processName)), '-');
}
protected function reOrderFields(array $fields, array $orderColumns)
{
$keyFields = array_keys($fields);
$sortedData = [];
// Menyimpan elemen yang ada di $data1 sesuai urutan di $data2
foreach ($orderColumns as $item) {
if (in_array($item, $keyFields)) {
$sortedData[] = $item;
unset($keyFields[array_search($item, $keyFields)]);
}
}
// Menambahkan elemen yang tersisa dari $data1 di akhir
$sortedData = array_merge($sortedData, $keyFields);
// ambil data yang ada di $fields sesuai urutan di $sortedData
$sortedData = collect($sortedData)
->mapWithKeys(
function ($field, $key) use ($fields) {
return [$field => $fields[$field]];
}
)
->toArray();
return $sortedData;
}
protected function orderingFields(array $fields, array $overrides)
{
$listOrder = $overrides['list_order_columns'];
$createOrder = $overrides['create_order_columns'];
$editOrder = $overrides['edit_order_columns'];
$keyFields = array_keys($fields);
// Check elements that are not in list order and reset the list order
$listUnOrderData = array_values(array_diff($keyFields, $listOrder));
$createUnOrderData = array_values(array_diff($keyFields, $createOrder));
$editUnOrderData = array_values(array_diff($keyFields, $editOrder));
$listOrderCount = count($listOrder);
$createOrderCount = count($createOrder);
$editOrderCount = count($editOrder);
// loops list order columns and add key list_order to fields with value from key list_order
foreach ($listOrder as $index => $field) {
$fields[$field]['list_order'] = $index + 1;
}
// loops create order columns and add key create_order to fields with value from key create_order
foreach ($createOrder as $index => $field) {
$fields[$field]['create_order'] = $index + 1;
}
// loops edit order columns and add key edit_order to fields with value from key edit_order
foreach ($editOrder as $index => $field) {
$fields[$field]['edit_order'] = $index + 1;
}
// loops list unorder columns and add key list_order to fields with value from key list_order
foreach ($listUnOrderData as $index => $field) {
$fields[$field]['list_order'] = $listOrderCount + $index + 1;
}
// loops create unorder columns and add key create_order to fields with value from key create_order
foreach ($createUnOrderData as $index => $field) {
$fields[$field]['create_order'] = $createOrderCount + $index + 1;
}
// loops edit unorder columns and add key edit_order to fields with value from key edit_order
foreach ($editUnOrderData as $index => $field) {
$fields[$field]['edit_order'] = $editOrderCount + $index + 1;
}
return $fields;
}
}
<?php
namespace Laravolt\Crud\Input;
use Laravolt\Crud\Contracts\CustomFieldContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
class BaseCustomFieldAtribute implements CustomFieldContract
{
use TableSpecHelper;
protected $table = "";
protected $method = "";
protected $overrides = [];
protected $path = "";
protected $name = "";
protected $type = "INPUT_TABLE";
public function __construct(string $name, string $table, array $overrides, $path)
{
$this->table = $table;
$this->name = $name;
$this->overrides = $overrides;
$this->path = $path;
}
function getValue()
{
return [
"table" => $this->table,
"name" => $this->name,
"type" => $this->type,
"spec" => $this->getTableSpec($this->table, $this->overrides, $this->path)
];
}
}
<?php
namespace Laravolt\Crud\Input;
use Laravolt\Crud\Contracts\CustomFieldContract;
use Laravolt\Crud\Contracts\FeFormatSpecContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
class BaseFeFormatAtribute implements FeFormatSpecContract
{
protected $name;
protected $format;
/**
* @param $name
* @param $format
*/
public function __construct($name, $format)
{
$this->name = $name;
$this->format = $format;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
public function getValue()
{
return [
'name' => $this->name,
'format' => $this->format
];
}
}
<?php
namespace Laravolt\Crud\Input;
use Laravolt\Crud\Contracts\CustomFieldContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
use Laravolt\Crud\Spec\BaselanguageSpec;
class BaseUploadFileFieldAtribute implements CustomFieldContract
{
use TableSpecHelper;
protected $service = "";
protected $name = "";
protected $type = "INPUT_FILE_UPLOAD";
protected $isMultiple = true;
protected $actionMessage = "Klik Untuk Mengunggah Atau Drag File";
protected $fileTypeMessage = "File yang bisa diupload adalah ";
protected $uploadingMessage = "Mengunggah ...";
protected $uploadErrorHeader = "Gagal Menggungah";
protected $uploadErrorMessage = "Terjadi kesalahan ketika mengunggah file";
protected $fileTypeErrorMessage = "Type file tidak bisa di upload ";
protected $fileSizeExceedHeader = "Size File Terlalu Besar";
protected $fileSizeExceedMessage = "Size file lebih besar dari yang di perbolehkan";
protected $allowedExtension = [];
public function __construct(string $name, string $service, bool $isMultiple = true, array $allowedExtension = [],
$actionMessage = null,
$fileTypeMessage = null)
{
$this->name = $name;
$this->service = $service;
$this->allowedExtension = $allowedExtension;
$this->isMultiple = $isMultiple;
$defaultclang = config("alurkerjalang.default") ?? "ID";
$lang = BaselanguageSpec::loadFromArray(config("alurkerjalang.$defaultclang"));
$this->actionMessage = $actionMessage ?? $lang->getLang("upload_action_message");
$this->fileTypeMessage = $fileTypeMessage ?? $lang->getLang("upload_file_type_message");
$this->uploadingMessage = $lang->getLang("upload_uploading_message");
$this->uploadErrorMessage = $lang->getLang("upload_upload_error_message");
$this->uploadErrorHeader = $lang->getLang("upload_upload_error_header");
$this->fileTypeErrorMessage = $lang->getLang("upload_file_type_error_message");
$this->fileSizeExceedHeader = $lang->getLang("upload_file_size_exceed_header");
$this->fileSizeExceedMessage = $lang->getLang("upload_file_size_exceed_message");
}
/**
* @param string $service
*/
public function setService(string $service): self
{
$this->service = $service;
return $this;
}
/**
* @param string $name
*/
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* @param string $type
*/
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
/**
* @param bool $isMultiple
*/
public function setIsMultiple(bool $isMultiple): self
{
$this->isMultiple = $isMultiple;
return $this;
}
/**
* @param mixed|string $actionMessage
*/
public function setActionMessage(mixed $actionMessage): self
{
$this->actionMessage = $actionMessage;
return $this;
}
/**
* @param mixed|string $fileTypeMessage
*/
public function setFileTypeMessage(mixed $fileTypeMessage): self
{
$this->fileTypeMessage = $fileTypeMessage;
return $this;
}
/**
* @param string $uploadingMessage
*/
public function setUploadingMessage(string $uploadingMessage): self
{
$this->uploadingMessage = $uploadingMessage;
return $this;
}
/**
* @param string $uploadErrorHeader
*/
public function setUploadErrorHeader(string $uploadErrorHeader): self
{
$this->uploadErrorHeader = $uploadErrorHeader;
return $this;
}
/**
* @param string $uploadErrorMessage
*/
public function setUploadErrorMesage(string $uploadErrorMessage): self
{
$this->uploadErrorMessage = $uploadErrorMessage;
return $this;
}
/**
* @param array $allowedExtension
*/
public function setAllowedExtension(array $allowedExtension): self
{
$this->allowedExtension = $allowedExtension;
return $this;
}
/**
* @param string $fileTypeErrorMessage
*/
public function setFileTypeErrorMessage(string $fileTypeErrorMessage): self
{
$this->fileTypeErrorMessage = $fileTypeErrorMessage;
return $this;
}
/**
* @param string $fileSizeExceedHeader
*/
public function setFileSizeExceedHeader(string $fileSizeExceedHeader): self
{
$this->fileSizeExceedHeader = $fileSizeExceedHeader;
return $this;
}
/**
* @param string $fileSizeExceedMessage
*/
public function setFileSizeExceedMessage(string $fileSizeExceedMessage): self
{
$this->fileSizeExceedMessage = $fileSizeExceedMessage;
return $this;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
function getValue()
{
return [
"type" => $this->type,
"name" => $this->name,
"is_multiple" => $this->isMultiple,
"allowed_extension" => $this->allowedExtension,
"action_message" => $this->actionMessage,
"file_type_message" => $this->fileTypeMessage,
"uploading_message" => $this->uploadingMessage,
"upload_error_header" => $this->uploadErrorHeader,
"upload_error_message" => $this->uploadErrorMessage,
"service" => $this->service,
];
}
}
<?php
namespace Laravolt\Crud\Input;
use Laravolt\Crud\Contracts\CustomFieldContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
class BaseUploadImageFieldAtribute extends BaseUploadFileFieldAtribute implements CustomFieldContract
{
use TableSpecHelper;
protected $type = "INPUT_IMAGE_UPLOAD";
}
<?php
namespace Laravolt\Crud\Input\Selection;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class StaticRadioSelection extends StaticSelection implements SelectionContract
{
protected $type = "INPUT_RADIO";
}
<?php
namespace Laravolt\Crud\Input\Selection;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class StaticSelection implements SelectionContract
{
protected $options = [];
protected $option_key = "";
protected $option_label = "";
protected $type = "INPUT_SELECT";
/**
* @param array $options
* @param string $option_key
* @param string $option_label
*/
public function __construct(array $options, string $option_key, string $option_label)
{
$this->options = $options;
$this->option_key = $option_key;
$this->option_label = $option_label;
}
function getValue()
{
return [
"options" => $this->options,
"option_key" => $this->option_key,
"type" => $this->type,
"option_label" => $this->option_label];
}
}
<?php
namespace Laravolt\Crud\Input\Selection;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
use Laravolt\Crud\Helper\TableSpecHelper;
class TableSelection implements SelectionContract
{
use TableSpecHelper;
protected $table = "";
protected $method = "";
protected $overrides = [];
protected $path = "";
protected $type = "INPUT_TABLE";
public function __construct(string $table, array $overrides, $path)
{
$this->table = $table;
$this->overrides = $overrides;
$this->path = $path;
}
function getValue()
{
return [
"table" => $this->table,
"type" => $this->type,
"spec" => $this->getTableSpec($this->table, $this->overrides, $this->path)
];
}
}
<?php
namespace Laravolt\Crud\Input\Selection;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class UrlForeignSelection implements SelectionContract
{
protected $url = "";
protected $method = "";
protected $option_key = "";
protected $option_label = "";
protected $type = "INPUT_FOREIGN-SELECT";
/**
* @param array $url
* @param string $table
* @param string $method
* @param string $option_key
* @param string $option_label
*/
public function __construct(string $url, string $method, string $option_key, string $option_label)
{
$this->url = $url;
$this->method = $method;
$this->option_key = $option_key;
$this->option_label = $option_label;
}
function getValue()
{
return [
"url" => $this->url,
"method" => $this->method,
"type" => $this->type,
"option_key" => $this->option_key,
"option_label" => $this->option_label];
}
}
<?php
namespace Laravolt\Crud\Input\Selection;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class UrlSelection implements SelectionContract
{
protected $url = "";
protected $table = "";
protected $method = "";
protected $option_key = "";
protected $option_label = "";
protected $type = "INPUT_SELECT";
/**
* @param array $url
* @param string $table
* @param string $method
* @param string $option_key
* @param string $option_label
*/
public function __construct(string $url, string $table, string $method, string $option_key, string $option_label)
{
$this->url = $url;
$this->table = $table;
$this->method = $method;
$this->option_key = $option_key;
$this->option_label = $option_label;
}
function getValue()
{
return [
"url" => $this->url,
"table" => $this->table,
"method" => $this->method,
"type" => $this->type,
"option_key" => $this->option_key,
"option_label" => $this->option_label];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\Mixin;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Arr;
/** @mixin \Illuminate\Database\Eloquent\Builder */
class QueryBuilderMixin
{
public function firstOrFail()
{
return function () {
$result = $this->first();
if (! $result) {
abort(404);
}
return $result;
};
}
public function whereLike()
{
return function ($attributes, ?string $searchTerm) {
if ($searchTerm) {
$searchTerm = addslashes(strtolower($searchTerm));
$this->where(function (Builder $query) use ($attributes, $searchTerm) {
foreach (Arr::wrap($attributes) as $column) {
$query->orWhereRaw(sprintf("LOWER(%s) LIKE '%%%s%%'", $column, $searchTerm));
}
});
}
return $this;
};
}
public function autoFilter()
{
return function () {
foreach (request('filter', []) as $column => $value) {
if ($value) {
/** @phpstan-ignore-next-line */
$this->whereLike($column, $value);
}
}
return $this;
};
}
public function autoSort()
{
return function ($sortByKey = 'sort', $sortDirectionKey = 'direction') {
$direction = request()->get($sortDirectionKey, 'asc');
if (request()->has($sortByKey)) {
$column = request()->get($sortByKey);
$this->orderBy($column, $direction);
}
return $this;
};
}
}
<?php
namespace Laravolt\Crud\Models;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Laravolt\Crud\CrudModel;
class ActivityLog extends CrudModel
{
protected $table = "camunda.activity_logs";
protected $casts = [
'decision' => AsCollection::class,
'status_after' => AsCollection::class,
];
protected $with = [
'updatedByUser',
];
public function loggable(): MorphTo
{
return $this->morphTo();
}
public function updatedByUser(): BelongsTo
{
return $this->belongsTo(config('auth.providers.users.model'), 'updated_by', 'id');
}
}
<?php
namespace Laravolt\Crud\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Laravolt\Crud\CrudModel;
class BpmnTask extends CrudModel
{
use HasFactory;
protected $table = 'camunda.v_bpmn_tasks';
protected array $filterableColumns = ['process_instance_id',
'process_definition_key',
'business_key', 'task_id', 'task_definition_key',
'task_name', 'start_time', 'end_time', 'status', 'parent_process_id', ];
}
<?php
namespace Laravolt\Crud;
use Spatie\RouteDiscovery\Attributes\Where;
enum PrimaryKeyFormat
{
case NUMERIC;
case UUID;
case AUTO;
public static function getRouteConstraint(self $format): string
{
return match ($format) {
self::NUMERIC => Where::numeric,
self::UUID => Where::uuid,
self::AUTO => "AUTO",
};
}
}
<?php
namespace Laravolt\Crud\Response\AlurKerja;
use Illuminate\Http\Resources\Json\PaginatedResourceResponse as BasePaginatedResourceResponse;
class PaginatedResourceResponse extends BasePaginatedResourceResponse
{
public function toArray()
{
return $this->resource->resource->toArray();
}
protected function paginationInformation($request)
{
$pagination = parent::paginationInformation($request);
unset($pagination['meta'], $pagination['links']);
return $pagination;
}
}
<?php
namespace Laravolt\Crud\Response\AlurKerja;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Resources\Json\JsonResource;
class Resource extends JsonResource
{
public function with($request)
{
$status = 200;
if ($this->resource instanceof Model && $this->resource->wasRecentlyCreated) {
$status = 201;
}
return [
'status' => $status,
'message' => 'success',
];
}
}
<?php
namespace Laravolt\Crud\Response\AlurKerja;
use Illuminate\Http\Resources\Json\ResourceCollection as BaseResourceCollection;
use Illuminate\Pagination\AbstractCursorPaginator;
use Illuminate\Pagination\AbstractPaginator;
class ResourceCollection extends BaseResourceCollection
{
public function with($request)
{
return [
'status' => 200,
'message' => 'success',
];
}
public function toArray($request)
{
$response = ['data' => $this->collection];
if ($this->resource instanceof AbstractPaginator || $this->resource instanceof AbstractCursorPaginator) {
$response = ['data' => ['content' => $this->collection]];
$pagination = (new PaginatedResourceResponse($this))->toArray();
$response['data'] += [
'pageable' => [
// 'sort' => [
// 'sorted' => $request->has('sort'),
// 'unsorted' => ! $request->has('sort'),
// 'empty' => $this->collection->isEmpty(),
// ],
'offset' => $pagination['per_page'] * ($pagination['current_page'] - 1),
// 'pageNumber' => $pagination['current_page'] - 1,
// 'pageSize' => $pagination['per_page'],
'paged' => true,
'unpaged' => false,
],
'total_page' => $pagination['last_page'],
'total_elements' => $pagination['total'],
'number_of_element' => $pagination['total'],
'first' => $pagination['current_page'] === 1,
'last' => $pagination['current_page'] === $pagination['last_page'],
'size' => $pagination['per_page'],
'number' => $pagination['current_page'] - 1,
'empty' => $pagination['total'] === 0,
'sort' => [
'sorted' => $request->has('sort'),
'unsorted' => ! $request->has('sort'),
'empty' => $this->collection->isEmpty(),
],
];
}
return $response;
}
/**
* Create a paginate-aware HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
protected function preparePaginatedResponse($request)
{
if ($this->preserveAllQueryParameters) {
$this->resource->appends($request->query());
} elseif (! is_null($this->queryParameters)) {
$this->resource->appends($this->queryParameters);
}
return (new PaginatedResourceResponse($this))->toResponse($request);
}
}
<?php
namespace Laravolt\Crud\Response\AlurKerja;
use Illuminate\Contracts\Support\Responsable;
class ValidationException extends \Illuminate\Validation\ValidationException implements Responsable
{
public $status = 400;
public function toResponse($request)
{
$data = [];
foreach ($this->errors() as $key => $errors) {
foreach ($errors as $error) {
$data[] = [$key => $error];
}
}
return response()->json(
[
'status' => $this->status,
'message' => $this->getMessage(),
'data' => $data,
],
$this->status
);
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class BulkResponse extends JsonResource
{
private const STATUS = 200;
protected array $invalid = [];
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function withInvalid(array $invalid): static
{
$this->invalid = $invalid;
return $this;
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => null,
'data' => $this->resource,
'invalid_actions' => $this->invalid,
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class DeleteFail extends JsonResource
{
private const STATUS = 422;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => 'error',
'data' => 'Object deletion failed',
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class DeleteSuccess extends JsonResource
{
private const STATUS = 200;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => 'success',
'data' => 'Object was deleted',
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class DomainViolationResponse extends JsonResource
{
private const STATUS = 400;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => $this->resource,
'data' => [],
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class Forbidden extends JsonResource
{
private const STATUS = 403;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => 'This action is unauthorized',
'data' => [
'url' => $request->fullUrl(),
'payload' => $request->json()->all(),
],
];
}
}
<?php
namespace Laravolt\Crud\Response\Laravolt;
use Illuminate\Http\Resources\Json\JsonResource;
class Resource extends JsonResource
{
public function with($request)
{
return [
'status' => 200,
'message' => 'success',
];
}
}
<?php
namespace Laravolt\Crud\Response\Laravolt;
use Illuminate\Http\Resources\Json\ResourceCollection as BaseResourceCollection;
class ResourceCollection extends BaseResourceCollection
{
public function with($request)
{
return [
'status' => 200,
'message' => 'success',
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class NoContent extends JsonResource
{
private const STATUS = 204;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => 'no_content',
'data' => null,
];
}
}
<?php
namespace Laravolt\Crud\Response;
use Illuminate\Http\Resources\Json\JsonResource;
class NotFound extends JsonResource
{
private const STATUS = 404;
public function withResponse($request, $response)
{
$response->setStatusCode(self::STATUS);
}
public function toArray($request)
{
return [
'status' => self::STATUS,
'message' => $this->resource->getMessage(),
'data' => $this->resource->getTrace(),
];
}
}
<?php
namespace Laravolt\Crud\RouteDiscovery;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Laravolt\Crud\CrudManager;
use Laravolt\Crud\PrimaryKeyFormat;
use Spatie\RouteDiscovery\Attributes\Where;
use Spatie\RouteDiscovery\PendingRoutes\PendingRoute;
use Spatie\RouteDiscovery\PendingRoutes\PendingRouteAction;
use Spatie\RouteDiscovery\PendingRouteTransformers\PendingRouteTransformer;
class HandleCrudRoutes implements PendingRouteTransformer
{
private array $crudMethods = [
'index',
'scope',
'spec',
'show',
'task',
'submitTask',
'create',
'edit',
'store',
'update',
'bulk',
'destroy',
];
private array $excepts = [
'__construct',
'model',
'setFormat',
'authorize',
'authorizeForUser',
'service',
'authorizeResource',
'callAction',
'toBpmnVariables',
'rules',
'guard'
];
public function transform(Collection $pendingRoutes): Collection
{
return $pendingRoutes->each(function (PendingRoute $pendingRoute) {
$pendingRoute->actions = $pendingRoute
->actions
->reject(fn (PendingRouteAction $action) => in_array($action->action[1], $this->excepts, true))
->sortBy(function (PendingRouteAction $action) {
return array_search($action->method->getName(), $this->crudMethods, true);
});
$pendingRoute->actions->each(function (PendingRouteAction $action) {
$crudAction = $action->action[1];
$hasId = collect($action->method->getParameters())->first(function (\ReflectionParameter $param) {
return $param->getName() === 'id';
});
$hasSlug = collect($action->method->getParameters())->first(function (\ReflectionParameter $param) {
return $param->getName() === 'slug';
});
if ($hasId) {
$action->uri = "$action->uri/{id}";
if (CrudManager::getPrimaryKeyFormat() != PrimaryKeyFormat::AUTO) {
$constraint = PrimaryKeyFormat::getRouteConstraint(CrudManager::getPrimaryKeyFormat());
$action->addWhere(new Where('id', $constraint));
} else {
$controller = new $action->action[0]();
$keyType = $controller->model()->getKeyType();
// Check if the model uses UUIDs for primary keys
if ($keyType === 'int') {
$action->addWhere(new Where('id', Where::numeric));
} else {
$action->addWhere(new Where('id', Where::uuid));
}
}
}
if ($hasSlug) {
$action->uri = "$action->uri/{slug}";
}
if (!in_array($crudAction, ['spec', 'import', 'export'])) {
if (!Str::of($action->name)->endsWith($crudAction)) {
$action->name = "$action->name.$crudAction";
}
}
if ($crudAction === 'scope') {
$action->uri = "{$action->uri}/{scope}";
$action->addWhere(new Where('scope', Where::alphanumeric));
}
if ($crudAction === 'task') {
$action->uri = Str::of($action->uri)->replace('/task/{id}', '/{id}/task');
}
if ($crudAction === 'submitTask') {
// dd($action->uri);
$action->uri = Str::of($action->uri)->replace('/submit-task/{taskId}', '/task/{taskId}/submit');
// dd($action->uri);
}
if ($crudAction === 'media') {
$action->uri = Str::of($action->uri)->replace('/media/{id}', '/{id}/media');
}
if ($crudAction === 'media') {
$action->uri = Str::of($action->uri)->replace('/media/{id}', '/{id}/media');
}
});
});
}
}
<?php
namespace Laravolt\Crud\Schema;
use Laravolt\Crud\Schema\Columns\Column;
use Laravolt\Crud\Schema\Columns\StringColumn;
class ColumnFactory
{
public static function make(\Doctrine\DBAL\Schema\Column $column): Column
{
$class = ucfirst($column->getType()->getName());
$classpath = "\\Laravolt\\Crud\\Schema\\Columns\\{$class}Column";
if (class_exists($classpath)) {
return new $classpath($column);
}
return new StringColumn($column);
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
use Doctrine\DBAL\Schema\Column as DoctrineColumn;
use Illuminate\Support\Str;
abstract class BaseColumn implements Column
{
protected DoctrineColumn $column;
protected string $inputType = 'text';
public function __construct(DoctrineColumn $column)
{
$this->column = $column;
}
public function spec(): array
{
$titleForHuman = Str::of($this->column->getName())->title()->replace('_', ' ');
return [
'name' => strtolower($this->column->getName()),
'label' => $titleForHuman->value(),
'required' => $this->column->getNotnull(),
'type' => $this->inputType(),
'constraints' => [],
'metadata' => [
'format' => null,
'canSort' => true,
'placeholder' => $titleForHuman,
'canFilter' => true,
],
];
}
public function lowcodespec(): array
{
$allowedFilterable = ["varchar", "text"];
$titleForHuman = Str::of($this->column->getName())->title()->replace('_', ' ');
return [
'name' => strtolower($this->column->getName()),
'label' => $titleForHuman->value(),
'required' => $this->column->getNotnull(),
'searchable' => in_array( $this->inputType(), $allowedFilterable),
'filterable' => false,
'sortable' => true,
'type' => $this->inputType(),
'form_field_type' => "INPUT_" . strtoupper($this->inputType()),
'primary' => $this->column->getAutoincrement(),
"is_hidden_in_create" => $this->column->getAutoincrement(),
"is_hidden_in_edit" => $this->column->getAutoincrement(),
"is_hidden_in_list" => false,
"is_hidden_in_detail" => false,
"rules" => $this->rules(),
"format" => $this->getFeFormat(),
"prefix" => "",
"suffix" => ""
//
// {
// "id": "63db511575f8fc542d5e06fc",
// "name": "id",
// "label": "Id",
// "type": "input_text",
// "isPrimary": true,
// "isRequired": true,
// "isHiddenInCreate": false,
// "isHiddenInEdit": false,
// "isHiddenInList": false,
// "format": "",
// "prefix": "",
// "suffix": ""
// },
];
}
public function getFeFormat() : string {
return "";
}
public function rules(): array
{
$nullable = $this->column->getNotnull() === false;
$hasDefault = $this->column->getDefault() !== null;
if ($nullable) {
return ['nullable'];
}
if (!$hasDefault) {
return ['required'];
}
return [];
}
public function inputType(): string
{
return $this->inputType ?? $this->column->getType()->getName();
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class BigintColumn extends IntegerColumn
{
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class BooleanColumn extends BaseColumn
{
protected string $inputType = 'switch';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'boolean';
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
interface Column
{
public function spec(): array;
public function rules(): array;
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class DateColumn extends BaseColumn
{
protected string $inputType = 'date';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'date_format:Y-m-d';
return $rules;
}
public function getFeFormat () :string{
return "DD-MM-YYYY";
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class DatetimeColumn extends BaseColumn
{
protected string $inputType = 'datetime-local';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'date_format:Y-m-d H:i:s';
return $rules;
}
public function getFeFormat () :string{
return "DD-MM-YYYY HH:mm:ss";
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class DecimalColumn extends BaseColumn
{
protected string $inputType = 'number';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'numeric';
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class EnumColumn extends BaseColumn
{
protected string $inputType = 'select';
public function rules(): array
{
$rules = parent::rules();
$this->getEnum();
$rules[] = 'string';
$max = $this->column->getLength();
if ($max) {
$rules[] = "max:$max";
}
return $rules;
}
public function getEnum() {
// TODO: cari cara untuk ambil data dari enum
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class FloatColumn extends DecimalColumn
{
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class IntegerColumn extends BaseColumn
{
protected string $inputType = 'number';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'integer';
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class JsonColumn extends BaseColumn
{
protected string $inputType = 'json';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'json';
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class SmallintColumn extends IntegerColumn
{
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class StringColumn extends BaseColumn
{
protected string $inputType = 'text';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'string';
$max = $this->column->getLength();
if ($max) {
$rules[] = "max:$max";
}
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class TextColumn extends BaseColumn
{
protected string $inputType = 'textarea';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'string';
return $rules;
}
}
<?php
namespace Laravolt\Crud\Schema\Columns;
class TimeColumn extends BaseColumn
{
protected string $inputType = 'time';
public function rules(): array
{
$rules = parent::rules();
$rules[] = 'date_format:H:i';
return $rules;
}
public function getFeFormat(): string
{
return "HH:mm:ss"; // TODO: Change the autogenerated stub
}
}
<?php
namespace Laravolt\Crud\Spec;
use Laravolt\Crud\Contracts\FieldSpecContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class BaseSpec implements FieldSpecContract
{
protected $name = "";
protected $label = "";
protected $required = false;
protected $searchable = false;
protected $filterable = false;
protected $sortable = false;
protected $type = "";
protected $form_field_type = "";
protected $primary = false;
protected $is_hidden_in_create = false;
protected $is_hidden_in_edit = false;
protected $is_hidden_in_list = false;
protected $rules = [];
protected $format = "";
protected $prefix = "";
protected $suffix = "";
/**
* @param string $name
* @param string $label
* @param bool $required
* @param bool $searchable
* @param bool $filterable
* @param bool $sortable
* @param string $type
* @param string $form_field_type
* @param bool $primary
* @param bool $is_hidden_in_create
* @param bool $is_hidden_in_edit
* @param bool $is_hidden_in_list
* @param array $rules
* @param string $format
* @param string $prefix
* @param string $suffix
*/
public function __construct(string $name, string $label, bool $required, bool $searchable, bool $filterable, bool $sortable, string $type, string $form_field_type, bool $primary, bool $is_hidden_in_create, bool $is_hidden_in_edit, bool $is_hidden_in_list, array $rules, string $format, string $prefix, string $suffix)
{
$this->name = $name;
$this->label = $label;
$this->required = $required;
$this->searchable = $searchable;
$this->filterable = $filterable;
$this->sortable = $sortable;
$this->type = $type;
$this->form_field_type = $form_field_type;
$this->primary = $primary;
$this->is_hidden_in_create = $is_hidden_in_create;
$this->is_hidden_in_edit = $is_hidden_in_edit;
$this->is_hidden_in_list = $is_hidden_in_list;
$this->rules = $rules;
$this->format = $format;
$this->prefix = $prefix;
$this->suffix = $suffix;
}
public static function createInstanceFromArray($specData)
{
$instance = new self(
"",
"",
false,
false,
false,
false,
"",
"",
false,
false,
false,
false,
[],
"",
"",
"",
);
foreach ($specData as $index => $value) {
$instance->$index = $value;
}
return $instance;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
function getValue()
{
return [
"name" => $this->name,
"label" => $this->label,
"required" => $this->required,
"searchable" => $this->searchable,
"filterable" => $this->filterable,
"sortable" => $this->sortable,
"type" => $this->type,
"form_field_type" => $this->form_field_type,
"primary" => $this->primary,
"is_hidden_in_create" => $this->is_hidden_in_create,
"is_hidden_in_edit" => $this->is_hidden_in_edit,
"is_hidden_in_list" => $this->is_hidden_in_list,
"rules" => $this->rules,
"format" => $this->format,
"prefix" => $this->prefix,
"suffix" => $this->suffix,
];
}
}
<?php
namespace Laravolt\Crud\Spec;
use Laravolt\Crud\Contracts\FieldSpecContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\Contracts\TableValueContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class BaseTableValue implements TableValueContract
{
protected $name = "";
protected $type = "";
protected $relation = "";
protected $value = "";
/**
* @param string $type
* @param string $relation
* @param string $value
*/
public function __construct(string $name, string $type, string $relation, string $value)
{
$this->name = $name;
$this->type = $type;
$this->relation = $relation;
$this->value = $value;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
public function getValue()
{
return [
'name' => $this->name,
'type' => $this->type,
'relation' => $this->relation,
'value' => $this->value,
];
}
}
<?php
namespace Laravolt\Crud\Spec;
use Laravolt\Crud\Contracts\FieldSpecContract;
use Laravolt\Crud\Contracts\SelectionContract;
use Laravolt\Crud\CrudModel;
use Laravolt\Crud\Enum\PermissionLevel;
class BaselanguageSpec
{
protected $create_button_label = "Tambah %s";
protected $detail_button_label = "Detail %s";
protected $edit_button_label = "Edit %s";
protected $delete_button_label = "Delete %s";
protected $start_process_button_label = "Start Process %s";
protected $create_then_start_process_button_label = "Tambah Dan Start Process %s";
protected $submit_task_button_label = "Submit Task %s";
protected $submit_task_confirm_message = "Apakah anda yakin ingin submit task untuk data ini?";
protected $submit_task_confirm_header = "Submit Task";
protected $submit_task_confirm_conirm_label = "Lanjutkan";
protected $submit_task_confirm_cancel_label = "Batal";
protected $delete_confirm_message = "Apakah anda yakin ingin menghapus data ini?1";
protected $delete_confirm_header = "Hapus Data";
protected $delete_confirm_conirm_label = "Lanjutkan";
protected $delete_confirm_cancel_label = "Batal";
protected $start_process_confirm_message = "Apakah anda yakin ingin submit process untuk data ini?";
protected $start_process_confirm_header = "Submit Process";
protected $start_process_confirm_conirm_label = "Lanjutkan";
protected $start_process_confirm_cancel_label = "Batal";
protected $field_list_description = "Field Dari %s";
protected $upload_action_message = "Klik Untuk Mengunggah Atau Drag File";
protected $upload_file_type_message = "File yang bisa diupload adalah ";
protected $upload_uploading_message = "Mengunggah ...";
protected $upload_upload_error_header = "Gagal Menggungah";
protected $upload_upload_error_message = "Terjadi kesalahan ketika mengunggah file";
protected $upload_file_type_error_message = "Type file tidak bisa di upload ";
protected $upload_file_size_exceed_header = "Size File Terlalu Besar";
protected $upload_file_size_exceed_message = "Size file lebih besar dari yang di perbolehkan";
protected $pagination_info = "Memunculkan data dari %s sampai %s total %s";
protected $empty_data_message = "Tidak ada data yang ditampilkan";
protected $filter_title = "Filter";
protected $filter_submit = "Filter";
protected $filter_reset = "Clear Filter";
protected $filter_cancel = "Kembali";
public function toArray()
{
return get_object_vars($this);
}
public static function loadFromArray($data){
$instance = new self();
foreach ($data as $index => $value) {
$instance->$index = $value;
}
return $instance;
}
public function getLang($path, mixed ...$values){
return sprintf($this->$path , ...$values);
}
}
<?php
namespace Laravolt\Crud\Sys\ActivityLog;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Laravolt\Crud\Traits\WithUuidPrimary;
use Laravolt\Suitable\Columns\Id;
use Ramsey\Uuid\Uuid;
class AkActivityLog extends Model
{
use HasFactory;
protected $table = "ak_activity_log";
protected $primaryKey = 'id';
// protected $primaryKey = 'id';
protected $keyType = "UUID";
public $incrementing = false;
protected $fillable = [
'class_name',
'entity_id',
'log_type',
'payload',
'action',
'busniess_key',
'status_after',
'status_before',
'act_by_name',
'decision',
'decision_remark',
'latest'
];
public static function createBpmnLog(
$data,
$business_key,
$status_after,
$status_before, $decision,
$decisionremark,
): void
{
$class_name = get_class($data);
$entity_id = $data->id;
$payload = json_encode(request()->all());
self::insert([
"id" => Uuid::uuid4()->toString(),
"class_name" => $class_name,
"entity_id" => $entity_id,
"business_key" => (string) $business_key,
"log_type" => "BPMN",
"action" => "START_PROCESS",
"payload" => $payload,
"status_after" => $status_after,
"status_before" => $status_before,
"act_by_name" => Auth::user() ? Auth::user()->id : -9999,
"decision" => $decision,
"decision_remark" => $decisionremark,
"latest" => true,
"created_at" => Carbon::now(),
"created_by" => Auth::user() ? Auth::user()->id : 0,
]);
}
public static function createUserTaskLog(
$data,
$business_key,
$status_after,
$status_before,
$decision,
$decisionremark,
$action = "SUBMIT_TASK"
): void
{
$class_name = get_class($data);
$entity_id = $data->id;
$payload = json_encode(request()->all());
self::insert([
"id" => Uuid::uuid4()->toString(),
"class_name" => $class_name,
"entity_id" => $entity_id,
"business_key" => (string) $business_key,
"log_type" => "USER_TASK",
"action" => $action,
"payload" => $payload,
"status_after" => $status_after,
"status_before" => $status_before,
"act_by_name" => Auth::user() ? Auth::user()->id : -9999,
"decision" => $decision,
"decision_remark" => $decisionremark,
"latest" => true,
"created_at" => Carbon::now(),
"created_by" => Auth::user() ? Auth::user()->id : 0,
]);
}
public static function createCrudLog(
$data,
$action,
): void
{
$class_name = get_class($data);
$entity_id = $data->id;
$payload = json_encode(request()->all());
self::insert([
"id" => Uuid::uuid4()->toString(),
"class_name" => $class_name,
"entity_id" => (string) $entity_id,
"business_key" => $entity_id,
"action" => $action,
"log_type" => "CRUD",
"act_by_name" => Auth::user() ? Auth::user()->id : -9999,
"latest" => true,
"created_at" => Carbon::now(),
"created_by" => Auth::user() ? Auth::user()->id : 0,
]);
}
}
<?php
namespace Laravolt\Crud\Sys\Notification;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class AkNotifUser extends Model
{
use HasFactory;
//$table->id();
//$table->string("title");
//$table->string("message");
//$table->text("payload");
//$table->string("url_to");
//$table->string("user_id");
//$table->date("read_at")->nullable();
//$table->date("sent_at")->nullable();
protected $fillable = [
"title",
"message",
"payload",
"url_to",
"user_id",
"read_at",
"sent_at"
];
public static function sendNotif($title, $message, $payload, $url_to, $user_id)
{
try {
event(new \App\Events\NewNotification($user_id));
} catch (\Exception $exception) {
Log::error($exception->getMessage());
}
self::create([
"title" => $title,
"message" => $message,
"payload" => json_encode($payload),
"url_to" => $url_to,
"user_id" => $user_id,
"read_at" => null,
"sent_at" => Carbon::now()
]);
}
public static function readNotif($id)
{
$data = self::find($id);
try {
event(new \App\Events\NewNotification($data->user_id));
} catch (\Exception $exception) {
Log::error($exception->getMessage());
}
$data->update([
"read_at" => Carbon::now()
]);
}
}
<?php
namespace Laravolt\Crud\Tests;
use App\Models\Kategori;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Laravolt\Crud\CrudModel;
use Tests\TestCase;
class BaseLowcodeBpmnTest extends TestCase
{
protected $processDefinitionKey = "";
protected $baseurl = "";
public function createData($data)
{
$url = $this->baseurl;
$response = $this
->post($url, $data);
return $response->json("data.id");
}
public function submitUserTask($busniessKey, $id, $userTask, $data)
{
$url = $this->baseurl . '/' . $userTask . '/' . $busniessKey . '/task/' . $id . "/submit";
$response = $this
->post($url, $data);
return $response;
}
public function createProcessInstance($id, $variables)
{
$url = $this->baseurl . '/' . $id . "/start";
$data = $variables;
// dd($url);
$response = $this
->post($url, $data);
return $response->json("data.process_instance_id");
}
}
<?php
namespace Laravolt\Crud\Tests;
use App\Models\Kategori;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Laravolt\Crud\CrudModel;
use Tests\TestCase;
class BaseLowcodeCrudTest extends TestCase
{
protected $endpoint = '';
public function createPayload()
{
throw new Exception("Please Implemement Me !!", 1);
}
public function updatePayload()
{
throw new Exception("Please Implemement Me !!", 1);
}
public function failedCreatePayload()
{
throw new Exception("Please Implemement Me !!", 1);
}
public function failedUpdatePayload()
{
throw new Exception("Please Implemement Me !!", 1);
}
public function updatedData(): CrudModel
{
throw new Exception("Please Implemement Me !!", 1);
}
public function deletedData(): CrudModel
{
throw new Exception("Please Implemement Me !!", 1);
}
/**
* A basic feature test example.
*/
public function testList(): void
{
$url = 'api' . $this->endpoint;
$response = $this->get($url);
$response->assertStatus(200);
}
public function testSpec(): void
{
$url = 'api' . $this->endpoint . '/spec';
$response = $this->get($url);
$response->assertStatus(200);
}
public function testDetail(): void
{
$data = $this->updatedData();
$url = 'api' . $this->endpoint . '/' . $data->id;
$response = $this->get($url);
$response->assertStatus(200);
}
public function testStore(): void
{
$url = 'api' . $this->endpoint;
$paylaod = $this->createPayload();
$response = $this->post($url, $paylaod);
$response->assertStatus(201);
}
public function testStoreFailed(): void
{
$url = 'api' . $this->endpoint;
$paylaod = $this->failedCreatePayload();
$response = $this->post($url, $paylaod);
$response->assertStatus(302);
}
public function testUpdate(): void
{
$url = 'api' . $this->endpoint;
$response = $this->get($url);
$data = $this->updatedData();
$paylaod = $this->updatePayload();
$response = $this->post($url . "/" . $data->id, $paylaod);
$data->refresh();
$response->assertStatus(200);
}
public function testUpdateFailed(): void
{
$url = 'api' . $this->endpoint;
$response = $this->get($url);
$data = $this->updatedData();
$paylaod = $this->failedUpdatePayload();
$response = $this->post($url . "/" . $data->id, $paylaod);
$data->refresh();
$response->assertStatus(302);
}
public function testDelete(): void
{
$url = 'api' . $this->endpoint;
$response = $this->get($url);
$data = $this->deletedData();
$response = $this->delete($url . "/" . $data->id);
$response->assertStatus(200);
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
/** @mixin Builder */
trait AutoFilter
{
private function isJsonColumn($castField, $column)
{
$jsonFieldName = $column;
if (Str::contains($column, '.')) {
$jsonFieldName = Str::beforeLast($column, '.');
}
// Filter item, with key == column and value is array
$found = collect($castField)->filter(function ($value, $key) use ($jsonFieldName) {
return $key === $jsonFieldName && $value === "array";
});
return sizeof($found) > 0;
}
public function scopeAutoFilter(Builder $query, $filter = 'filter')
{
// Only apply filter defined in $filterableColumns
$filterPayload = collect(request($filter, []))->only($this->getFilterableColumns() ?? []);
$castField = $this->casts ?: []; // default kosong takut undefined
foreach ($filterPayload as $column => $value) {
if ($this->isJsonColumn($castField, $column)) {
$column = str_replace(".", "->", (string) $column);
$query->whereJsonContains($column, $value);
continue;
}
if (Str::contains($column, '.')) {
$relationName = Str::beforeLast($column, '.');
$column = Str::afterLast($column, '.');
/** @phpstan-ignore-next-line */
$query->leftJoinRelationship($relationName);
// Get fully qualified column name
$lastRelationName = Str::afterLast($relationName, '.');
$relatedTable = $this->$lastRelationName()->getModel()->getTable();
$column = "$relatedTable.$column";
}
if (is_string($value) || is_null($value)) {
if ($value === ($this->filterNotNull ?? '(filled)')) {
$query->whereNotNull($column);
} else {
$query->where($column, $value);
}
} elseif (is_array($value)) {
$query->whereIn($column, Arr::flatten($value));
}
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
/** @mixin Builder */
trait AutoSearch
{
public function scopeAutoSearch(Builder $query, ?string $keyword)
{
if ($keyword !== null) {
$dbDriver = DB::connection()->getConfig('driver');
if ($dbDriver === 'pgsql') {
$columns = Arr::map($this->getSearchableColumns(), fn ($elm) => $elm.'::text');
$query->whereLike($columns, $keyword);
} else {
$query->whereLike($this->getSearchableColumns(), $keyword);
}
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Kirschbaum\PowerJoins\PowerJoins;
trait AutoSort
{
use PowerJoins;
public function scopeAutoSort(Builder $query, $payload = null, $sortByKey = 'sort', $sortDirectionKey = 'direction')
{
if ($payload === null) {
$payload = request()->all();
}
$payload = collect($payload);
$direction = Arr::get($payload, $sortDirectionKey, 'asc');
// support for AlurKerja sort format
if ($payload->has('asc')) {
$direction = filter_var($payload->get('asc'), FILTER_VALIDATE_BOOLEAN) ? 'asc' : 'desc';
}
if (Arr::get($payload, $sortByKey)) {
$columnQueryString = $column = Arr::get($payload, $sortByKey);
if (Str::contains($columnQueryString, '.')) {
$relationName = Str::beforeLast($columnQueryString, '.');
$query->select($this->getTable().'.*');
/** @phpstan-ignore-next-line */
$query->leftJoinRelationship($relationName);
/** @phpstan-ignore-next-line */
$query->orderByPowerJoins($columnQueryString, $direction);
} else {
$query->orderBy($column, $direction);
}
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Laravolt\Crud\VariableType\BooleanType;
use Laravolt\Crud\VariableType\DoubleType;
use Laravolt\Crud\VariableType\IntegerType;
use Laravolt\Crud\VariableType\JsonType;
use Laravolt\Crud\VariableType\ObjectType;
use Laravolt\Crud\VariableType\StringType;
trait CanFormatBpmnVariables
{
public function toBpmnVariables(array $data): array
{
$variables = [];
foreach ($data as $key => $value) {
$valueType = gettype($value);
$typeClass = match ($valueType) {
'array' => JsonType::class,
'boolean' => BooleanType::class,
'object' => ObjectType::class,
'double' => DoubleType::class,
'integer' => IntegerType::class,
default => StringType::class,
};
$variables[$key] = (new $typeClass())($value);
}
return $variables;
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection as BaseResourceCollection;
use Illuminate\Support\Collection;
use Laravolt\Crud\ApiFormat;
use Laravolt\Crud\Response\AlurKerja\ResourceCollection;
use Laravolt\Crud\Response\Laravolt\Resource;
trait CanFormatResource
{
protected static string $resource = Resource::class;
protected static string $resourceCollection = ResourceCollection::class;
protected function single(Collection | Model $model): JsonResource
{
return app(self::$resource, ['resource' => $model]);
}
protected function collection(Collection|LengthAwarePaginator $collection): BaseResourceCollection
{
return app(self::$resourceCollection, ['resource' => $collection]);
}
public static function setFormat(ApiFormat $format): void
{
switch ($format) {
case ApiFormat::AlurKerja:
self::$resource = \Laravolt\Crud\Response\AlurKerja\Resource::class;
self::$resourceCollection = \Laravolt\Crud\Response\AlurKerja\ResourceCollection::class;
break;
case ApiFormat::Laravolt:
self::$resource = Resource::class;
self::$resourceCollection = ResourceCollection::class;
break;
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
trait CanHandleMedia
{
private function processMediaable($request, $model, $methods = 'CREATED'): void
{
foreach ($model->getMediableColumns() as $column) {
$mediaId = collect($request->input($column))->pluck('id');
Media::query()
->whereIn('id', $mediaId)
->update([
'model_type' => get_class($model),
'model_id' => $model->getKey(),
'collection_name' => $column,
]);
if (! $model->isIgnoreMediaDeletion()) {
if ($methods == 'UPDATE') {
Media::query()
->whereNotIn('id', $mediaId)->where('model_type', get_class($model))
->where('model_id', $model->getKey())
->where('collection_name', $column)
->where('disk', 'public')
->delete();
}
}
}
}
//
// private function processNexcloudable($request, $model, $methods = 'CREATED'): void
// {
// /** @var \App\Models\User $user */
// $user = auth()->user();
// $nextcloud = new NextcloudFs();
// $className = explode('\\', get_class($model));
// $module = $className[count($className) - 1];
// foreach ($model->getNexcloudableColumns() as $column) {
// $mediaId = collect($request->input($column))->pluck('id');
// if ($mediaId->isEmpty()) {
//// if media id empty ketika update maka hapus semua media
// if (! $model->isIgnoreMediaDeletion()) {
// if ($methods == 'UPDATE') {
// Media::where('model_type', get_class($model))
// ->where('model_id', $model->getKey())
// ->where('collection_name', $column)
// ->where('disk', 'nextcloud')
// ->delete();
// }
// }
// }
//
// $nextcloud->createShareFolder($user, $column, $model);
// Media::whereIn('id', $mediaId)
// ->get()
// ->map(function ($media) use ($model, $nextcloud, $user, $module, $column) {
// $uploaded = $nextcloud->uploadToNextcloud($user, $media, $column, $model);
// if ($uploaded) {
// $media->update([
// 'model_type' => get_class($model),
// 'model_id' => $model->getKey(),
// 'disk' => 'nextcloud',
// 'collection_name' => $column,
// 'custom_properties' => [
// 'module' => $module,
// 'userFolder' => $user->keycloak_id,
// 'uploadUrl' => $uploaded,
// ],
// ]);
// } else {
// $media->update([
// 'model_type' => get_class($model),
// 'model_id' => $model->getKey(),
// 'collection_name' => $column,
// ]);
// }
// });
//
// if (! $model->isIgnoreMediaDeletion()) {
// if ($methods == 'UPDATE') {
// Media::whereNotIn('id', $mediaId)->where('model_type', get_class($model))
// ->where('model_id', $model->getKey())
// ->where('collection_name', $column)
// ->where('disk', 'nextcloud')
// ->delete();
// }
// }
//// Media::whereNotIn('id', $mediaId)->where("model_type", get_class($model))->where("model_id", $model->getKey())->delete();
// }
// }
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Laravolt\Crud\Contracts\ToExcel;
use Spatie\RouteDiscovery\Attributes\Route;
use Spatie\SimpleExcel\SimpleExcelWriter;
/** @mixin \Laravolt\Crud\ApiCrudController */
trait ExportAllToExcel
{
#[Route(method: ['GET'])]
public function export(Request $request): void
{
/** @var \Laravolt\Crud\Contracts\ToExcel $model */
$model = $this->model();
if (! $model instanceof ToExcel) {
throw new \DomainException(sprintf('Exported model need to implement %s', ToExcel::class));
}
$table = class_basename($model);
$filename = sprintf('%s-%s.xlsx', $table, date('YmdHis'));
$writer = SimpleExcelWriter::streamDownload($filename);
$data = $this->service->all($request);
if ($data->isEmpty()) {
$writer->addRow($model->toExcel());
}
foreach ($data as $i => $model) {
$writer->addRow($model->toExcel());
// Flush the buffer every 1000 rows
if ($i % 1000 === 0) {
flush();
}
}
$writer->toBrowser();
}
/**
* Ubah nested filter menjadi flat: bpmnTasks.task_definition_key => task_definition_key
*
* @param array $filter
*
* @return array
*/
protected function normalizeFilterForExport(array $filter)
{
$normalizedFilter = [];
foreach ($filter as $field => $value) {
$field = Str::of($field)->afterLast('.')->toString();
$normalizedFilter[$field] = $value;
}
return $normalizedFilter;
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Spatie\RouteDiscovery\Attributes\Route;
use Spatie\SimpleExcel\SimpleExcelWriter;
/** @mixin \Laravolt\Crud\ApiCrudController */
trait ExportLazyToExcel
{
#[Route(method: ['GET'])]
public function export(Request $request): void
{
/** @var \Laravolt\Crud\Contracts\LazyExport $model */
$model = $this->model();
$table = $model->getLazyExportView();
$filename = sprintf('%s-%s.xlsx', $table, date('d-m-Y'));
$writer = SimpleExcelWriter::streamDownload($filename);
request()?->merge([
'filter' => $this->service->validateFilters($this->normalizeFilterForExport(request('filter', [])))
]);
foreach (DB::table($table)->autoFilter()->orderBy('id')->lazy() as $i => $item) {
try {
$writer->addRow(json_decode(json_encode($item, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR));
} catch (\JsonException $exception) {
$writer->addRow([$exception->getMessage()]);
}
// Flush the buffer every 1000 rows
if ($i % 1000 === 0) {
flush();
}
}
$writer->toBrowser();
}
/**
* Ubah nested filter menjadi flat: bpmnTasks.task_definition_key => task_definition_key
*
* @param array $filter
*
* @return array
*/
protected function normalizeFilterForExport(array $filter)
{
$normalizedFilter = [];
foreach ($filter as $field => $value) {
$field = Str::of($field)->afterLast('.')->toString();
$normalizedFilter[$field] = $value;
}
return $normalizedFilter;
}
}
<?php
namespace Laravolt\Crud\Traits;
use App\Models\Media;
use Illuminate\Support\Facades\Log;
//use Laravolt\Crud\Nextcloud\NextcloudFs;
trait GeneratorTrait
{
public function checkAndGenerateFolder ($path) {
// Create Folder if not exsist
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
}
public function generateFile ($class, $stubPath, $destPath , $params = [], $replace = false) {
$modelStub = file_get_contents($stubPath);
foreach ($params as $key => $value) {
$modelStub = str_replace("{".$key."}", $value, $modelStub);
}
$dest = $destPath. $class.".php";;
$isExsist = file_exists($dest) ;
if( $isExsist && !$replace) {
echo("File $dest Exsis\n" );
}else {
file_put_contents($destPath. $class.".php", $modelStub);
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
//use Laravolt\Crud\Nextcloud\NextcloudFs;
use Laravolt\Crud\Response\DomainViolationResponse;
use Laravolt\Crud\Response\NotFound;
use Spatie\RouteDiscovery\Attributes\Route;
/** @mixin \Laravolt\Crud\ApiCrudController */
trait MediaUploader
{
#[Route(method: ['POST'])]
public function media(Request $request, mixed $id): JsonResource
{
try {
$model = $this->service->find($id);
$this->authorize('update', $model);
$medias = collect();
foreach ($model->getMediableColumns() as $column) {
if (($files = $request->file($column)) === null) {
continue;
}
if (! is_array($files)) {
$files = [$files];
}
foreach ($files as $file) {
/** @phpstan-ignore-next-line */
$medias->push($model->addMedia($file)->toMediaCollection($column));
}
}
/** @var \App\Models\User $user */
$user = auth()->user();
// $nextcloud = new NextcloudFs();
$className = explode('\\', get_class($model));
$module = $className[count($className) - 1];
foreach ($model->getNexcloudableColumns() as $column) {
if (($files = $request->file($column)) === null) {
continue;
}
if (! is_array($files)) {
$files = [$files];
}
foreach ($files as $file) {
/** @phpstan-ignore-next-line */
$media = $model->addMedia($file)->toMediaCollection($column);
$uploaded = $nextcloud->uploadToNextcloud($user, $media, $column, $model);
if ($uploaded) {
$media->update([
'model_type' => get_class($model),
'model_id' => $model->getKey(),
'disk' => 'nextcloud',
'collection_name' => $column,
'custom_properties' => [
'module' => $module,
'userFolder' => $user->keycloak_id,
'uploadUrl' => $uploaded,
],
]);
}
$medias->push($media);
}
}
if ($medias->isEmpty()) {
return new DomainViolationResponse('Media wajib diisi');
}
return $this->collection($medias);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
}
}
}
<?php
namespace Laravolt\Crud\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
use Ramsey\Uuid\Uuid as Generator;
/** @mixin Builder */
trait WithUuidPrimary
{
protected $primaryKey = 'id';
protected $keyType = PrimaryKeyFormat::UUID;
public $incrementing = false;
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
try {
$model->id = Generator::uuid4()->toString();
} catch (UnsatisfiedDependencyException $e) {
abort(500, $e->getMessage());
}
});
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Camunda\Http\TaskClient;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Contracts\UsertaskControllerContract;
use Laravolt\Crud\Exceptions\CrudException;
use Laravolt\Crud\Response\DomainViolationResponse;
use Laravolt\Crud\Response\NoContent;
use Laravolt\Crud\Response\NotFound;
use Laravolt\Crud\Traits\CanFormatBpmnVariables;
use Nette\NotImplementedException;
use Spatie\RouteDiscovery\Attributes\Route;
abstract class UserTaskController extends ApiCrudController implements UsertaskControllerContract
{
use CanFormatBpmnVariables;
abstract public function model(): UserTaskModel;
protected function service(): UserTaskService
{
return new UserTaskService($this->model(), $this->user);
}
/**
* GET. [US] Detail Task By Model ID
*/
public function task(Request $request, mixed $id): JsonResource
{
try {
/** @var \Laravolt\Crud\UserTaskModel $model */
$model = $this->service->find($id);
if (! $model instanceof UserTaskModel) {
throw new \DomainException(sprintf('Class %s harus extends %s', get_class($model), UserTaskModel::class));
}
$this->authorize('view', $model);
$tasks = collect(TaskClient::getByProcessInstanceId($model->getProcessInstanceId()));
return $this->collection($tasks);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
}
}
/**
* PUT. [US] Update Data
*/
#[Route(method: 'PUT', uri: 'task')]
public function updateTask(Request $request, string $id): JsonResource
{
try {
/** @var \Laravolt\Crud\BpmnModel $model */
$model = $this->service->find($id);
$this->authorize('update', $model);
$this->service()->update($id, $request);
return $this->single($model);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
} catch (\DomainException $e) {
return new DomainViolationResponse($e->getMessage());
}
}
/**
* POST. [US] Submit Task
*/
#[Route(method: 'POST', uri: '{businessKey}/submit-task/{taskId}')]
public function submitTask(Request $request, mixed $businessKey, mixed $taskId): JsonResource
{
try {
$model = $this->service()->submitTask($taskId, $request, $businessKey);
return $this->single($model);
} catch (ModelNotFoundException $e) {
return new NotFound($e);
} catch (\DomainException $e) {
return new DomainViolationResponse($e->getMessage());
}
}
#[Route(method: 'POST')]
public function assigneeCandidate(Request $request): JsonResource
{
$result = $this->service()->assigneeCandidate($request);
return $this->collection($result);
}
#[Route(method: 'POST', uri: 'assign/{busniess_key}/{task_id}')]
public function assign(Request $request , $busniess_key , $task_id)
{
$validated = $request->validate([
'user_id' => 'required',
]);
$result = $this->service()->assign($request->user_id , $task_id , $busniess_key);
if($result) {
return response(
$request->all(),
200,
['Content-Type' => 'application/json']
);
}else {
return response(
$request->all(),
400,
['Content-Type' => 'application/json']
);
}
}
#[Route(method: 'POST', uri: 'claim/{busniess_key}/{task_id}')]
public function claim(Request $request, $busniess_key, $task_id)
{
$result = $this->service()->claim($task_id, $busniess_key);
if($result) {
return response(
$request->all(),
200,
['Content-Type' => 'application/json']
);
}else {
return response(
$request->all(),
400,
['Content-Type' => 'application/json']
);
}
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Laravolt\Crud\Models\ActivityLog;
abstract class UserTaskModel extends CrudModel
{
protected array $filterableColumns = [
'bpmnTasks.task_definition_key',
'bpmnTasks.process_definition_key',
];
protected string $businessKey = 'business_key';
protected bool $isUserTask = true;
protected bool $isUpdateOrCreate = true;
protected string $processInstanceKey = 'process_instance_id';
// protected string $userTask = 'process_instance_id';
public function getUserTaskName(): string
{
return (new \ReflectionClass(static::class))->getShortName();
}
/**
* Variable yang hanya akan tersimpan di camunda dan database
* @return array
*/
protected array $processVariables = [];
/**
* Variable yang hanya akan tersimpan di camunda
* @return array
*/
protected array $transientProcessVariables = [];
public function getProcessInstanceKey(): string
{
return $this->processInstanceKey;
}
public function getBusinessKeyFieldName(): string
{
return $this->businessKey;
}
public function getProcessInstanceId(): string|null
{
return $this->getAttribute($this->getProcessInstanceKey());
}
public function setProcessInstanceId(string $id): static
{
$this->{$this->getProcessInstanceKey()} = $id;
return $this;
}
public function processDefinitionKey(): string
{
return (new \ReflectionClass(static::class))->getShortName();
}
// public function bpmnTasks(): HasMany
// {
// return $this->hasMany(\Laravolt\Crud\Models\BpmnTask::class, 'process_instance_id', 'process_instance_id');
// }
public function getProcessVariables(): array
{
return $this->processVariables;
}
/**
* @return array
*/
public function getTransientProcessVariables(): array
{
return $this->transientProcessVariables;
}
public function getProcessVariablesRules(): array
{
return [];
//TODO mapping process variables ke masing2 step (start or user task tertentu)
// $rules = [];
// foreach ($this->getProcessVariables() as $var) {
// $rules[$var] = ['required'];
// }
//
// return $rules;
}
public function toProcessVariables(array $data = []): array
{
$variables = [];
foreach ($this->getProcessVariables() as $var) {
$value = $data[$var] ?? $this->getAttribute($var) ?? null;
if ($value !== null) {
$variables[$var] = $value;
}
}
return $variables;
}
public function getIsUpdateOrCreate(): bool
{
return $this->isUpdateOrCreate;
}
// public function logs(): MorphMany
// {
// return $this->morphMany(ActivityLog::class, 'loggable');
// }
}
<?php
namespace Laravolt\Crud;
use App\Models\Camunda\BpmnTask;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Laravolt\Camunda\Dto\Task;
use Laravolt\Camunda\Http\CamundaClient;
use Laravolt\Camunda\Http\ProcessDefinitionClient;
use Laravolt\Camunda\Http\ProcessInstanceClient;
use Laravolt\Camunda\Http\TaskClient;
use Laravolt\Crud\Contracts\StoreRequestContract;
use Laravolt\Crud\Response\DomainViolationResponse;
use Laravolt\Crud\Sys\ActivityLog\AkActivityLog;
use Laravolt\Crud\Traits\CanFormatBpmnVariables;
use Laravolt\Platform\Models\User;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
class UserTaskService extends CrudService
{
use CanFormatBpmnVariables;
// TODO: belum make sense karena ketika sudah ada datanya pasti task nya sudah maju kedepan (history)
// TODO: lebih oke buat history
// public function get(Request $request): LengthAwarePaginator
// {
// $data = parent::get($request); // TODO: Change the autogenerated stub
// $processInstance = [];
// $processInstance = $data->getCollection()->map(function ($processData) {
// if ($processData->parent != null) {
// return $processData->parent->process_instance_id;
// } else {
// return null;
// }
// // return $processData->process_instance_id;
// })->filter(function ($processData) {
// return $processData != null;
// });
// $tasks = collect(TaskClient::getByProcessInstanceIds(ids: $processInstance->toArray()));
// $data->getCollection()->transform(function ($processInstance) use ($tasks) {
// $processInstance['user_task'] = $tasks->filter(function ($taskInstance) use ($processInstance) {
// if ($processInstance->parent != null) {
// return $taskInstance->processInstanceId == $processInstance->parent->process_instance_id and
// $taskInstance->taskDefinitionKey == $processInstance->getUserTaskName();
// } else {
// return false;
// }
// // return $taskInstance->processInstanceId == $processInstance->process_instance_id;
// })->first();
// return $processInstance;
// });
// return $data;
// }
public function find(mixed $id): CrudModel
{
$model = parent::find($id);
if ($model?->parent?->process_instance_id) {
$tasks = collect(TaskClient::getByProcessInstanceId($model->parent->process_instance_id));
$model->available_task = $tasks->filter(function ($taskInstance) use ($model) {
return $taskInstance->processInstanceId == $model->parent->process_instance_id;
});
}
return $model;
}
public function assigneeCandidate($request)
{
// dd(User::all()->pluck("name", "id"));
return User::all()->map(function ($data) {
return [
"value" => $data->id,
"label" => $data->name,
];
});
}
public function start(StoreRequestContract $request): UserTaskModel
{
/** @phpstan-ignore-next-line */
Validator::make($request->all(), $this->model->getProcessVariablesRules())->validate();
return DB::transaction(function () use ($request) {
/** @var \Laravolt\Crud\UserTaskModel $model */
$model = $this->create($request);
$processInstance = ProcessDefinitionClient::start(
key: $model->processDefinitionKey(),
businessKey: $model->getKey(),
variables: $this->toBpmnVariables($model->toProcessVariables($request->all()))
);
$model->setProcessInstanceId($processInstance->id)->save();
$tasks = collect(TaskClient::getByProcessInstanceId($processInstance->id));
// $model->logs()->create([
// 'decision' => collect(['start' => true]),
// 'status_before' => null,
// 'status_after' => $tasks->pluck('taskDefinitionKey'),
// ]);
return $model;
});
}
public function startExisting(mixed $id, array $payload)
{
/** @var \Laravolt\Crud\UserTaskModel $model */
$model = $this->find($id);
// Sudah pernah di-start sebelumnya
if ($model->getProcessInstanceId()) {
return new DomainViolationResponse(
sprintf(
'Bpmn model with id %s already started with process instance ID %s',
$model->getKey(),
$model->getProcessInstanceId()
)
);
}
return DB::transaction(function () use ($model, $payload) {
$processInstance = ProcessDefinitionClient::start(
key: $model->processDefinitionKey(),
businessKey: $model->getKey(),
//TODO: how to submit process variables only
variables: $this->toBpmnVariables($model->toProcessVariables($payload))
);
$model->setProcessInstanceId($processInstance->id)->save();
return $model;
});
}
public function getUsertaskModel()
{
return $this->model;
}
public function beforeSubmitHook(string $taskId, Request $request, string $businessKey)
{
return $request;
}
public function afterSubmitHook(UserTaskModel $model, Request $request, string $businessKey): UserTaskModel
{
return $model;
}
public function prepareSubmitData(UserTaskModel $model, Request $request): array
{
$allowedPayload = array_diff(array_keys($model->rules($request)), $model->getTransientProcessVariables());
$payload = $request->only($allowedPayload);
$payload = array_merge($payload, $this->transformCreateRequest($request));
return $payload;
}
public function claim($task_id, $busniess_key)
{
$task = TaskClient::get(["id" => $task_id]);
if (sizeof($task) < 0) {
throw new BadRequestException("Task not found");
}
$task = $task[0];
$user_id = Auth::user()->id;
$tasks = TaskClient::claim($task_id, $user_id);
AkActivityLog::createUserTaskLog(
$this->model,
$busniess_key,
"NEW",
"Tobe defined",
json_encode($this->model->toProcessVariables(\request()->all())),
"Tobe defined",
"CLAIM_TASK"
);
return $tasks;
}
public function assign($user_id, $task_id, $busniess_key)
{
$task = TaskClient::find($task_id);
if (!$task) {
throw new BadRequestException("Task not found");
}
$tasks = TaskClient::assign($task_id, $user_id);
AkActivityLog::createUserTaskLog(
$task,
$busniess_key,
"NEW",
"Tobe defined",
json_encode($this->model->toProcessVariables(\request()->all())),
"Tobe defined",
"ASSIGN_TASK"
);
return $tasks;
}
public function storeData(UserTaskModel $model, Request $request, string $businessKey): UserTaskModel
{
$payload = $this->prepareSubmitData($model, $request);
$payload[$model->getBusinessKeyFieldName()] = $businessKey;
if ($model->getIsUpdateOrCreate()) {
/**
* Handle update or create
*
* @var \Laravolt\Crud\UserTaskModel
*/
$newModel = $model
->newQuery()
->where($model->getBusinessKeyFieldName(), $businessKey)
->first();
$model = $newModel ?? $model;
}
$model->fill($payload);
$model->save();
return $model;
}
public function submitTask(string $taskId, Request $request, string $businessKey): CrudModel
{
$request = $this->beforeSubmitHook($taskId, $request, $businessKey);
$model = $this->getUsertaskModel();
$task = TaskClient::find($taskId);
return DB::transaction(function () use ($task, $model, $request, $businessKey) {
// Only update attributes defined in rules to avoid undesired user input
// TODO how to validate request for current task
$model = $this->storeData($model, $request, $businessKey);
$model = $this->afterSubmitHook($model, $request, $businessKey);
/**
* Submit di bagian terakhir, untuk mencegah task id kesubumit dengan kasus
* storeData() berhasil tetapi afterSubmitHook terdapat error
*/
$variables = $request->only(array_merge($model->getProcessVariables(), $model->getTransientProcessVariables()));
$variables = ['_id' => $model->getKey()] + $variables;
$method = 'variableFor' . $task->taskDefinitionKey;
if (method_exists($model, $method)) {
$variables = $model->$method($variables);
}
Log::info(
'Submit Task',
['pid' => $task->processInstanceId, 'tid' => $task->id, 'variables' => $variables]
);
TaskClient::submit($task->id, $this->toBpmnVariables($variables));
$this->storeLog($businessKey, $model, $task, $request);
return $model;
});
}
public function storeLog(string $businessKey, UserTaskModel $model, Task $task, Request $request): void
{
AkActivityLog::createUserTaskLog(
$model,
$businessKey,
"NEW",
"Tobe defined",
json_encode($model->toProcessVariables($request->all())),
"Tobe defined",
);
}
//
// public function getBusinessKey(string $taskId): string
// {
// if (config('services.camunda.on_premise')) {
// $task = TaskClient::find(id: $taskId);
// $processInstance = ProcessInstanceClient::find(id: $task->processInstanceId);
// dd($processInstance);
// } else {
//
// $bpmnTask = BpmnTask::query()
// ->where('task_id', $taskId)
// ->first();
//
// if (!$bpmnTask) {
// throw new ModelNotFoundException('Task id not found');
// }
//
// return $bpmnTask->business_key;
// }
// }
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
class BooleanType
{
public function __invoke($value): array
{
return ['value' => (bool) $value, 'type' => 'Boolean'];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
class DoubleType
{
public function __invoke($value): array
{
return ['value' => $value, 'type' => 'Double'];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
class IntegerType
{
public function __invoke($value): array
{
return ['value' => $value, 'type' => 'Integer'];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
class JsonType
{
/**
* @throws \JsonException
*/
public function __invoke($value): array
{
return [
'value' => json_encode($value, JSON_THROW_ON_ERROR),
'type' => 'Json'
];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
use Illuminate\Support\Collection;
class ObjectType
{
/**
* @throws \JsonException
*/
public function __invoke($value): array
{
if ($value instanceof Collection) {
return [
'value' => $value->toJson(),
'type' => 'Object',
'valueInfo' => [
'objectTypeName' => 'java.util.Collection',
'serializationDataFormat' => 'application/json',
],
];
}
return [
'value' => json_encode($value, JSON_THROW_ON_ERROR),
'type' => 'Object',
];
}
}
<?php
declare(strict_types=1);
namespace Laravolt\Crud\VariableType;
class StringType
{
public function __invoke($value): array
{
return ['value' => (string) $value, 'type' => 'String'];
}
}
<?php
namespace Laravolt\Crud;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Support\Facades\View;
abstract class WebCrudController
{
use AuthorizesRequests, ValidatesRequests;
public function index()
{
return View::first([request()->route()->getName(), 'crud::index']);
}
public function show(mixed $id)
{
return View::first([request()->route()->getName(), 'crud::show']);
}
public function create()
{
return View::first([request()->route()->getName(), 'crud::create']);
}
public function edit(mixed $id)
{
return View::first([request()->route()->getName(), 'crud::edit']);
}
public function store(mixed $id)
{
}
public function destroy(mixed $id)
{
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment