Skip to content

Commit

Permalink
动态关联
Browse files Browse the repository at this point in the history
  • Loading branch information
slowlyo committed Mar 23, 2024
1 parent c377eb7 commit fcaf932
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 83 deletions.
16 changes: 9 additions & 7 deletions config/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,27 @@

'auth' => [
// 是否开启验证码
'login_captcha' => env('ADMIN_LOGIN_CAPTCHA', true),
'login_captcha' => env('ADMIN_LOGIN_CAPTCHA', true),
// 是否开启认证
'enable' => true,
'enable' => true,
// 是否开启鉴权
'permission' => true,
'guard' => 'admin',
'guards' => [
'permission' => true,
// token 有效期 (分钟), 为空则不限时
'token_expiration' => null,
'guard' => 'admin',
'guards' => [
'admin' => [
'driver' => 'sanctum',
'provider' => 'admin',
],
],
'providers' => [
'providers' => [
'admin' => [
'driver' => 'eloquent',
'model' => \Slowlyo\OwlAdmin\Models\AdminUser::class,
],
],
'except' => [
'except' => [

],
],
Expand Down
4 changes: 3 additions & 1 deletion src/AdminServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Psr\Container\NotFoundExceptionInterface;
use Psr\Container\ContainerExceptionInterface;
use Slowlyo\OwlAdmin\Models\PersonalAccessToken;
use Slowlyo\OwlAdmin\Support\{Context, Cores\Menu, Cores\Asset, Cores\Module};
use Slowlyo\OwlAdmin\Support\{Context, Cores\Menu, Cores\Asset, Cores\Module, Cores\Relationships};

class AdminServiceProvider extends ServiceProvider
{
Expand Down Expand Up @@ -78,6 +78,8 @@ public function boot(): void
$this->loadRoutes();
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
$this->usePersonalAccessTokenModel();

Relationships::loader();
}

protected function loadBaseRoute()
Expand Down
1 change: 1 addition & 0 deletions src/Controllers/AdminRoleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ protected function setPermission()
->name('permissions')
->label()
->multiple()
->heightAuto()
->options(AdminPermissionService::make()->getTree())
->searchable()
->cascade()
Expand Down
266 changes: 196 additions & 70 deletions src/Controllers/DevTools/RelationshipController.php

Large diffs are not rendered by default.

50 changes: 45 additions & 5 deletions src/Models/AdminRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Slowlyo\OwlAdmin\Models;

use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Concerns\HasTimestamps;

class AdminRelationship extends BaseModel
Expand Down Expand Up @@ -64,11 +66,6 @@ class AdminRelationship extends BaseModel
self::TYPE_MORPH_TO_MANY => '多对多(多态)',
];

public function aaa()
{
// $this->belongsTo();
}

public static function typeOptions()
{
return collect(self::TYPE_MAP)->map(function ($item, $index) {
Expand All @@ -79,4 +76,47 @@ public static function typeOptions()
];
})->values();
}

public function method(): Attribute
{
return Attribute::get(fn() => self::TYPE_MAP[$this->type]);
}

public function buildArgs()
{
$reflection = new \ReflectionClass($this->model);
$params = $reflection->getMethod($this->method)->getParameters();

$args = [];

foreach ($params as $item) {
$_value = data_get($this->args, $item->getName());
$args[] = [
'name' => $item->getName(),
'value' => filled($_value) ? $_value : $item->getDefaultValue(),
];
}

return $args;
}

public function getPreviewCode()
{
$className = Str::of($this->model)->explode('\\')->pop();
$args = collect($this->buildArgs())
->pluck('value')
->map(fn($item) => is_null($item) ? 'null' : (is_string($item) ? "'{$item}'" : $item))
->implode(', ');

return <<<PHP
<?php
class $className extends Model
{
public function $this->title() {
return \$this->$this->method($args);
}
}
PHP;
}
}
22 changes: 22 additions & 0 deletions src/Models/PersonalAccessToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,26 @@ public function __construct(array $attributes = [])

parent::__construct($attributes);
}

public static function findToken($token)
{
$expiration = config('admin.auth.token_expiration');

if (!str_contains($token, '|')) {
return static::where('token', hash('sha256', $token))
->when($expiration, fn($q) => $q->where('created_at', '>=', now()->subMinutes($expiration)))
->first();
}

[$id, $token] = explode('|', $token, 2);

$instance = static::when($expiration, fn($q) => $q->where('created_at', '>=', now()->subMinutes($expiration)))
->find($id);

if ($instance) {
return hash_equals($instance->token, hash('sha256', $token)) ? $instance : null;
}

return null;
}
}
109 changes: 109 additions & 0 deletions src/Services/AdminRelationshipService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Slowlyo\OwlAdmin\Services;

use Illuminate\Support\Str;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Slowlyo\OwlAdmin\Models\AdminRelationship;

Expand All @@ -14,4 +17,110 @@ class AdminRelationshipService extends AdminService
protected string $modelName = AdminRelationship::class;

public string $cacheKey = 'admin_relationships';

public function list()
{
$list = parent::list();

collect($list['items'])->transform(function ($item) {
$item->setAttribute('preview_code', $item->getPreviewCode());
});

return $list;
}

public function getAll()
{
return cache()->rememberForever($this->cacheKey, function () {
return self::query()->get();
});
}

public function saving(&$data, $primaryKey = '')
{
$exists = self::query()
->where('model', $data['model'])
->where('title', $data['title'])
->when($primaryKey, fn($q) => $q->where('id', '<>', $primaryKey))
->exists();

admin_abort_if($exists, '该模型下存在同名关联');

$methodExists = method_exists($data['model'], $data['title']);

admin_abort_if($methodExists, '该模型下存在同名关联');
}

public function saved($model, $isEdit = false)
{
cache()->forget($this->cacheKey);
}

public function deleted($ids)
{
cache()->forget($this->cacheKey);
}

public function allModels()
{
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(app_path('Models')));
$phpFiles = new \RegexIterator($iterator, '/^.+\.php$/i', \RegexIterator::GET_MATCH);

foreach ($phpFiles as $phpFile) {
$filePath = $phpFile[0];
require_once $filePath;
}

$modelDirClass = collect(get_declared_classes())
->filter(fn($i) => Str::startsWith($i, 'App\\Models'))
->toArray();

$composer = require base_path('/vendor/autoload.php');
$classMap = $composer->getClassMap();

$tables = collect(json_decode(json_encode(Schema::getAllTables()), true))
->map(fn($i) => array_shift($i))
->toArray();

$models = collect($classMap)
->keys()
->filter(fn($item) => str_contains($item, 'Models\\'))
->filter(fn($item) => @class_exists($item))
->filter(fn($item) => (new \ReflectionClass($item))->isSubclassOf(Model::class))
->merge($modelDirClass)
->unique()
->filter(fn($item) => in_array(app($item)->getTable(), $tables))
->values()
->map(fn($item) => [
'label' => Str::of($item)->explode('\\')->pop(),
'table' => app($item)->getTable(),
'value' => $item,
]);

return compact('tables', 'models');
}

public function generateModel($table)
{
$className = Str::of($table)->studly()->singular()->value();

$template = <<<PHP
<?php
namespace App\Models;
use Slowlyo\OwlAdmin\Models\BaseModel as Model;
class $className extends Model
{
protected \$table = '$table';
}
PHP;

$path = app_path("Models/$className.php");

admin_abort_if(file_exists($path), '模型已存在');

app('files')->put($path, $template);
}
}
28 changes: 28 additions & 0 deletions src/Support/Cores/Relationships.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Slowlyo\OwlAdmin\Support\Cores;

use Slowlyo\OwlAdmin\Services\AdminRelationshipService;

class Relationships
{
public static function loader()
{
$relationships = AdminRelationshipService::make()->getAll();

if (blank($relationships)) {
return;
}

foreach ($relationships as $relationship) {
try {
$relationship->model::resolveRelationUsing($relationship->title, function ($model) use ($relationship) {
$method = $relationship->method;

return $model->$method(...array_column($relationship->buildArgs(), 'value'));
});
}catch (\Throwable $e) {
}
}
}
}
3 changes: 3 additions & 0 deletions src/Support/Cores/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ private static function baseRoutes($prefix)
$router->resource('relationships', RelationshipController::class);
$router->group(['prefix' => 'relation'], function (Router $router) {
$router->get('model_options', [RelationshipController::class, 'modelOptions']);
$router->get('column_options', [RelationshipController::class, 'columnOptions']);
$router->get('all_models', [RelationshipController::class, 'allModels']);
$router->post('generate_model', [RelationshipController::class, 'generateModel']);
});
});
}
Expand Down

0 comments on commit fcaf932

Please sign in to comment.