mirror of
https://github.com/archtechx/airwire.git
synced 2025-12-12 10:44:03 +00:00
initial
This commit is contained in:
commit
d26fa93f1e
35 changed files with 2388 additions and 0 deletions
122
src/Concerns/ManagesActions.php
Normal file
122
src/Concerns/ManagesActions.php
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace Airwire\Concerns;
|
||||
|
||||
use Airwire\Airwire;
|
||||
use ReflectionMethod;
|
||||
use ReflectionObject;
|
||||
use Illuminate\Foundation\Exceptions\Handler;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Throwable;
|
||||
|
||||
trait ManagesActions
|
||||
{
|
||||
public array $readonly = [];
|
||||
|
||||
public function makeChanges(array $changes): void
|
||||
{
|
||||
foreach ($this->getSharedProperties() as $property) {
|
||||
if (isset($changes[$property]) && ! $this->isReadonly($property)) {
|
||||
if (! $this->makeChange($property, $changes[$property])) {
|
||||
unset($changes[$property]);
|
||||
}
|
||||
} else {
|
||||
unset($changes[$property]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes) {
|
||||
try {
|
||||
$this->changed($changes);
|
||||
} catch (ValidationException) {}
|
||||
}
|
||||
}
|
||||
|
||||
protected function makeChange(string $property, mixed $new): bool
|
||||
{
|
||||
$old = $this->$property ?? null;
|
||||
|
||||
try {
|
||||
if ($this->updating($property, $new, $old) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method_exists($this, $method = ('updating' . ucfirst($property)))) {
|
||||
if ($this->$method($new, $old) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (ValidationException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->$property = $new;
|
||||
|
||||
$this->updated($property, $new);
|
||||
|
||||
if (method_exists($this, $method = ('updated' . ucfirst($property)))) {
|
||||
$this->$method($new, $old);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function makeCalls(array $calls): void
|
||||
{
|
||||
$this->metadata['calls'] ??= [];
|
||||
|
||||
foreach ($this->getSharedMethods() as $method) {
|
||||
if (isset($calls[$method])) {
|
||||
try {
|
||||
$result = $this->callWiredMethod($method, $calls[$method]);
|
||||
|
||||
if ($method === 'mount') {
|
||||
if (isset($result['readonly'])) {
|
||||
$readonly = $result['readonly'];
|
||||
unset($result['readonly']);
|
||||
$result = array_merge($readonly, $result);
|
||||
|
||||
$this->readonly = array_unique(array_merge(
|
||||
$this->readonly, array_keys($readonly)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->metadata['calls'][$method] = $result;
|
||||
} catch (Throwable $e) {
|
||||
if (! app()->isProduction() && ! $e instanceof ValidationException) {
|
||||
$reflection = (new ReflectionObject($handler = (new Handler(app()))))->getMethod('convertExceptionToArray');
|
||||
$reflection->setAccessible(true);
|
||||
|
||||
$this->metadata['exceptions'] ??= [];
|
||||
$this->metadata['exceptions'][$method] = $reflection->invoke($handler, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function callWiredMethod(string $method, array $arguments): mixed
|
||||
{
|
||||
$reflectionMethod = new ReflectionMethod($this, $method);
|
||||
$parameters = $reflectionMethod->getParameters();
|
||||
|
||||
foreach ($arguments as $index => &$value) {
|
||||
if (! isset($parameters[$index])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$parameter = $parameters[$index];
|
||||
|
||||
$value = Airwire::decode($parameter, $value);
|
||||
}
|
||||
|
||||
$result = $this->$method(...$arguments);
|
||||
|
||||
if ($returnType = $reflectionMethod->getReturnType()) {
|
||||
return Airwire::encode($returnType, $result);
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/Concerns/ManagesLifecycle.php
Normal file
57
src/Concerns/ManagesLifecycle.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Airwire\Concerns;
|
||||
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
trait ManagesLifecycle
|
||||
{
|
||||
public function hydrateComponent(): bool
|
||||
{
|
||||
if ($this->strictValidation && $this->validate(throw: false) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method_exists($this, 'hydrate')) {
|
||||
$hydrate = app()->call([$this, 'hydrate'], $this->requestState);
|
||||
|
||||
if (is_bool($hydrate)) {
|
||||
return $hydrate;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dehydrateComponent(): void
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
|
||||
if (method_exists($this, 'dehydrate')) {
|
||||
app()->call([$this, 'dehydrate'], $this->requestState);
|
||||
}
|
||||
} catch (ValidationException) {}
|
||||
|
||||
if (isset($this->errors) && ! $this->hasBeenReset) {
|
||||
$this->metadata['errors'] = $this->errors->toArray();
|
||||
} else {
|
||||
$this->metadata['errors'] = [];
|
||||
}
|
||||
|
||||
$this->metadata['readonly'] = array_unique(array_merge($this->readonly, $this->getReadonlyProperties()));
|
||||
}
|
||||
|
||||
public function updating(string $property, mixed $new, mixed $old): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updated(string $property, mixed $value): void
|
||||
{
|
||||
}
|
||||
|
||||
public function changed(array $changes): void
|
||||
{
|
||||
}
|
||||
}
|
||||
96
src/Concerns/ManagesState.php
Normal file
96
src/Concerns/ManagesState.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Airwire\Concerns;
|
||||
|
||||
use Airwire\Airwire;
|
||||
use ReflectionMethod;
|
||||
use ReflectionObject;
|
||||
use ReflectionProperty;
|
||||
use Airwire\Attributes\Wired;
|
||||
|
||||
trait ManagesState
|
||||
{
|
||||
public bool $hasBeenReset = false;
|
||||
|
||||
public function getSharedProperties(): array
|
||||
{
|
||||
return collect((new ReflectionObject($this))->getProperties())
|
||||
->filter(
|
||||
fn (ReflectionProperty $property) => collect($property->getAttributes(Wired::class))->isNotEmpty()
|
||||
)
|
||||
->map(fn (ReflectionProperty $property) => $property->getName())
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function getSharedMethods(): array
|
||||
{
|
||||
return collect((new ReflectionObject($this))->getMethods())
|
||||
->filter(
|
||||
fn (ReflectionMethod $method) => collect($method->getAttributes(Wired::class))->isNotEmpty()
|
||||
)
|
||||
->map(fn (ReflectionMethod $method) => $method->getName())
|
||||
->merge(method_exists($this, 'mount') ? ['mount'] : [])
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function getState(): array
|
||||
{
|
||||
return collect($this->getSharedProperties())
|
||||
->combine($this->getSharedProperties())
|
||||
->map(function (string $property) {
|
||||
if (isset($this->$property)) {
|
||||
return $this->$property;
|
||||
}
|
||||
|
||||
$default = optional((new ReflectionProperty($this, $property))->getAttributes(Wired::class))[0]->newInstance()->default;
|
||||
if ($default !== null) {
|
||||
return Airwire::decode([$this, $property], $default);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
public function getEncodedState(): array
|
||||
{
|
||||
return collect($this->getState())
|
||||
->map(fn ($value, $key) => Airwire::encode([$this, $key], $value))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function getReadonlyProperties(): array
|
||||
{
|
||||
return collect($this->getSharedProperties())
|
||||
->filter(fn (string $property) => $this->isReadonly($property))
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function isReadonly(string $property): bool
|
||||
{
|
||||
$attributes = (new ReflectionProperty($this, $property))->getAttributes(Wired::class);
|
||||
|
||||
return count($attributes) === 1 && $attributes[0]->newInstance()->readonly === true;
|
||||
}
|
||||
|
||||
public function reset(array $properties = null): void
|
||||
{
|
||||
$properties ??= $this->getSharedProperties();
|
||||
|
||||
foreach ($properties as $property) {
|
||||
unset($this->$property);
|
||||
}
|
||||
|
||||
$this->hasBeenReset = true;
|
||||
}
|
||||
|
||||
public function meta(string|array $key, mixed $value): void
|
||||
{
|
||||
if (is_array($key)) {
|
||||
$this->metadata = array_merge($this->metadata, $key);
|
||||
} else {
|
||||
$this->metadata[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/Concerns/ManagesValidation.php
Normal file
84
src/Concerns/ManagesValidation.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Airwire\Concerns;
|
||||
|
||||
use Illuminate\Contracts\Validation\Validator as AbstractValidator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
trait ManagesValidation
|
||||
{
|
||||
public bool $strictValidation = true;
|
||||
|
||||
public MessageBag $errors;
|
||||
|
||||
/** @throws ValidationException */
|
||||
public function validate(string|array $properties = null, bool $throw = true): bool
|
||||
{
|
||||
$validator = $this->validator($properties);
|
||||
|
||||
if ($validator->fails()) {
|
||||
if (isset($this->errors)) {
|
||||
foreach ($validator->errors()->toArray() as $property => $errors) {
|
||||
foreach ($errors as $error) {
|
||||
if (! in_array($error, $this->errors->get($property))) {
|
||||
$this->errors->add($property, $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->errors = $validator->errors();
|
||||
}
|
||||
|
||||
if ($throw) {
|
||||
$validator->validate();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @throws ValidationException */
|
||||
public function validated(string|array $properties = null): array
|
||||
{
|
||||
return $this->validator($properties)->validated();
|
||||
}
|
||||
|
||||
public function validator(string|array $properties = null): AbstractValidator
|
||||
{
|
||||
$state = array_merge($this->getState(), $this->changes);
|
||||
$rules = $this->rules();
|
||||
$messages = $this->messages();
|
||||
$attributes = $this->attributes();
|
||||
|
||||
$properties = $properties
|
||||
? Arr::wrap($properties)
|
||||
: $this->getSharedProperties();
|
||||
|
||||
$state = collect($state)->only($properties)->toArray();
|
||||
$rules = collect($rules)->only($properties)->toArray();
|
||||
$messages = collect($messages)->only($properties)->toArray();
|
||||
$attributes = collect($attributes)->only($properties)->toArray();
|
||||
|
||||
return Validator::make($state, $rules, $messages, $attributes);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return $this->rules ?? [];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function attributes()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue