diff --git a/extend/catcher/CatchAdmin.php b/extend/catcher/CatchAdmin.php new file mode 100644 index 0000000..78e5ade --- /dev/null +++ b/extend/catcher/CatchAdmin.php @@ -0,0 +1,325 @@ +getRootPath() . self::NAME . DIRECTORY_SEPARATOR; + } + + /** + * + * @time 2019年12月04日 + * @param $module + * @return string + */ + public static function moduleDirectory($module): string + { + $directory = self::directory() . $module . DIRECTORY_SEPARATOR; + + if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); + } + + return $directory; + } + + /** + * + * @time 2019年11月30日 + * @return string + */ + public static function cacheDirectory(): string + { + $directory = app()->getRuntimePath() . self::NAME . DIRECTORY_SEPARATOR; + + if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); + } + + return $directory; + } + + /** + * + * @time 2019年12月03日 + * @param $module + * @return string + */ + public static function moduleMigrationsDirectory($module): string + { + return self::directory() . $module . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR. 'migrations' . DIRECTORY_SEPARATOR; + } + + /** + * + * @time 2019年12月03日 + * @param $module + * @return string + */ + public static function moduleSeedsDirectory($module): string + { + return self::directory() . $module . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR. 'seeds' . DIRECTORY_SEPARATOR; + } + + /** + * 获取模块 view path + * + * @time 2019年12月03日 + * @param $module + * @return string + */ + public static function getModuleViewPath($module): string + { + $directory = self::directory() . $module . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR; + + if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); + } + + return $directory; + } + + /** + * + * @time 2019年12月03日 + * @param $module + * @return string + */ + public static function getModuleModelDirectory($module): string + { + $directory = self::directory() . $module . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR; + + if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); + } + + return $directory; + } + /** + * + * @time 2019年11月30日 + * @return array + */ + public static function getModulesDirectory(): array + { + $modules = glob(self::directory() . '*'); + + foreach ($modules as $key => &$module) { + if (!is_dir($module)) { + unset($modules[$key]); + } + + $module .= DIRECTORY_SEPARATOR; + } + + return $modules; + } + + /** + * + * @time 2019年11月30日 + * @return array + */ + protected static function getModuleServices(): array + { + $services = []; + + foreach (self::getModulesDirectory() as $module) { + if (is_dir($module)) { + $moduleInfo = self::getModuleInfo($module); + if (isset($moduleInfo['services']) && !empty($moduleInfo['services'])) { + $services = array_merge($services, $moduleInfo['services']); + } + } + } + + return $services; + } + + /** + * + * @time 2019年11月30日 + * @return array + */ + protected static function getModuleViews(): array + { + $views = []; + + foreach (self::getModulesDirectory() as $module) { + if (is_dir($module . 'view')) { + $moduleInfo = self::getModuleInfo($module); + $moduleName = $moduleInfo['alias'] ?? Arr::last(explode('/', $module)); + $views[$moduleName] = $module . 'view' . DIRECTORY_SEPARATOR; + } + } + + return $views; + } + + /** + * 获取模块信息 + * + * @time 2019年11月30日 + * @param $module + * @return mixed + */ + public static function getModuleInfo($module) + { + if (file_exists($module . DIRECTORY_SEPARATOR . 'module.json')) { + return \json_decode(file_get_contents($module . DIRECTORY_SEPARATOR . 'module.json'), true); + } + + return []; + } + + /** + * 获取服务 + * + * @time 2019年11月30日 + * @return array + */ + public static function getServices(): array + { + if (file_exists(self::getCacheServicesFile())) { + return self::getCacheServices(); + } + + return self::getModuleServices(); + } + + /** + * + * @time 2019年11月30日 + * @return string + */ + public static function getRoutes(): string + { + if (file_exists(self::getCacheViewsFile())) { + return self::getCacheRoutesFile(); + } + + self::cacheRoutes(); + + return self::getCacheRoutesFile(); + } + + /** + * + * @time 2019年11月30日 + * @return array|mixed + */ + public static function getViews() + { + if (file_exists(self::getCacheViewsFile())) { + return self::getCacheViews(); + } + + return self::getModuleViews(); + } + + /** + * + * @time 2019年11月30日 + * @return false|int + */ + public static function cacheRoutes() + { + $routeFiles = []; + foreach (self::getModulesDirectory() as $module) { + if (file_exists($module . 'route.php')) { + $routeFiles[] = $module . 'route.php'; + } + } + $routes = ''; + foreach ($routeFiles as $route) { + $routes .= trim(str_replace('fields[$this->name] = array_merge($this->fields[$this->name], [ + 'id' => $id, + ]); + return $this; + } + + + public function class($class='', $labelClass = '', $inlineClass = '') + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'class' => $class, + 'labelClass' => $labelClass, + 'inlineClass' => $inlineClass, + ]); + + return $this; + } + + public function options(array $options) + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'options' => $options, + ]); + + return $this; + } + + public function default($value) + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'default' => $value, + ]); + + return $this; + } + + public function disabled() + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'disabled' => '', + + ]); + + return $this; + } + public function placeholder($content) + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'placeholder' => 'placeholder='.$content, + ]); + + return $this; + } + + public function readonly() + { + $this->fields[$this->name] = array_merge($this->fields[$this->name], [ + 'readonly' => 'readonly', + ]); + + return $this; + } + + + public function render() + { + $form = ''; + foreach ($this->fields as $field) { + $form .= sprintf($this->baseField(), + $field['labelClass'] ?? '', + $field['label'], + $field['inlineClass'] ?? '', + $this->{$field['type'].'Field'}($field)); + + } + + return $form; + } + + + public function __call($method, $arguments) + { + // TODO: Implement __call() method. + $this->name = $arguments[0] ?? ''; + $label = $arguments[1] ?? ''; + + $this->fields[$this->name] = [ + 'name' => $this->name, + 'type' => $method, + 'label' => $label, + ]; + + return $this; + } + + protected function inline() + { + $this->fields[] = array_merge($this->fields, [ + 'inline' => true, + ]); + + return $this; + } + + private function baseField() + { + return + '
+ +
+ %s +
+
'; + } + private function textField($field) + { + return + sprintf('', + $field['name'], + $field['class'], + $field['default'] ?? '', + $field['readonly'] ?? '', + $field['placeholder'] ?? '', + $field['disabled'] ?? '' + ); + + } + + private function selectField($field) + { + $select = sprintf(''; + } + + private function radioField() + {} + + private function textareaField() + {} + + private function imageField() + {} + +} diff --git a/extend/catcher/CatchResponse.php b/extend/catcher/CatchResponse.php new file mode 100644 index 0000000..2323a15 --- /dev/null +++ b/extend/catcher/CatchResponse.php @@ -0,0 +1,39 @@ + $code, + 'msg' => $msg, + 'data' => $data, + ]); + } + + /** + * 错误的响应 + * + * @time 2019年12月02日 + * @param string $msg + * @param int $code + * @return \think\response\Json + */ + public static function fail($msg = '', $code = 10001): \think\response\Json + { + return json([ + 'code' => $code, + 'msg' => $msg, + ]); + } +} diff --git a/extend/catcher/Form.php b/extend/catcher/Form.php new file mode 100644 index 0000000..383c8eb --- /dev/null +++ b/extend/catcher/Form.php @@ -0,0 +1,7 @@ + CatchAdmin::getViews()[$this->getModule($end['class'])] + ]); + + return View::fetch($template ? : $this->getTemp($end['class'], $end['function']), $data); + + } + + /** + * + * @time 2019年12月03日 + * @param $class + * @param $func + * @return string + */ + protected function getTemp($class, $func) + { + $viewPath = CatchAdmin::getModuleViewPath($this->getModule($class)); + + $class = explode('\\', $class); + + $className = strtolower(end($class)); + + if (is_dir($viewPath . $className)) { + return sprintf('%s/%s', $className, $func); + } + + return $func; + } + + /** + * + * @time 2019年12月03日 + * @param $class + * @return mixed + */ + protected function getModule($class) + { + return explode('\\', $class)[1]; + } +} diff --git a/extend/catcher/base/BaseModel.php b/extend/catcher/base/BaseModel.php new file mode 100644 index 0000000..ad4e984 --- /dev/null +++ b/extend/catcher/base/BaseModel.php @@ -0,0 +1,20 @@ +validate(); + } + + /** + * 初始化验证 + * + * @time 2019年11月27日 + * @throws \Exception + * @return mixed + */ + protected function validate() + { + $validate = new Validate(); + + if (!$validate->check(request()->param(), $this->rules())) { + throw new ValidateFailedException($validate->getError()); + } + + return true; + } + + abstract protected function rules(): array; + + abstract protected function message(): array; +} diff --git a/extend/catcher/base/BaseValidate.php b/extend/catcher/base/BaseValidate.php new file mode 100644 index 0000000..db644e2 --- /dev/null +++ b/extend/catcher/base/BaseValidate.php @@ -0,0 +1,37 @@ +register(); + + $this->rule = $this->getRules(); + } + + + abstract protected function getRules(): array ; + + + private function register() + { + if (!empty($this->newValidates())) { + foreach ($this->newValidates() as $validate) { + $this->extend($validate->type(), [$validate, 'verify'], $validate->message()); + } + } + } + + + private function newValidates() + { + return [ + ]; + } +} diff --git a/extend/catcher/command/InstallCommand.php b/extend/catcher/command/InstallCommand.php new file mode 100644 index 0000000..8fb5f5c --- /dev/null +++ b/extend/catcher/command/InstallCommand.php @@ -0,0 +1,329 @@ +setName('install:project') + // ->addArgument('module', Argument::REQUIRED, 'module name') + ->setDescription('install project'); + } + + /** + * + * @time 2019年11月29日 + * @param Input $input + * @param Output $output + * @return int|void|null + */ + protected function execute(Input $input, Output $output) + { + $this->detectionEnvironment(); + + $this->firstStep(); + + $this->secondStep(); + + $this->thirdStep(); + + $this->finished(); + + $this->project(); + } + + /** + * 环境检测 + * + * @time 2019年11月29日 + * @return void + */ + protected function detectionEnvironment(): void + { + $this->output->info('environment begin to check...'); + + if (version_compare(PHP_VERSION, '7.1.0', '<')) { + $this->output->error('php version should >= 7.1.0'); + exit(); + } + + $this->output->info('php version ' . PHP_VERSION); + + if (!extension_loaded('mbstring')) { + $this->output->error('mbstring extension not install');exit(); + } + $this->output->info('mbstring extension is installed'); + + if (!extension_loaded('json')) { + $this->output->error('json extension not install'); + exit(); + } + $this->output->info('json extension is installed'); + + if (!extension_loaded('openssl')) { + $this->output->error('openssl extension not install'); + exit(); + } + $this->output->info('openssl extension is installed'); + + if (!extension_loaded('pdo')) { + $this->output->error('pdo extension not install'); + exit(); + } + $this->output->info('pdo extension is installed'); + + if (!extension_loaded('xml')) { + $this->output->error('xml extension not install'); + exit(); + } + + $this->output->info('xml extension is installed'); + + $this->output->info('🎉 environment checking finished'); + } + + /** + * 安装第一步 + * + * @time 2019年11月29日 + * @return bool + */ + protected function firstStep(): bool + { + if (file_exists($this->app->getRootPath() . '.env')) { + return false; + } + + $answer = strtolower($this->output->ask($this->input, '🤔️ Did You Need to Set Database information? (Y/N): ')); + + if ($answer === 'y' || $answer === 'yes') { + $charset = $this->output->ask($this->input, '👉 please input database charset, default (utf8mb4):') ? : 'utf8mb4'; + $database = ''; + while (!$database) { + $database = $this->output->ask($this->input, '👉 please input database name: '); + if ($database) { + break; + } + } + $host = $this->output->ask($this->input, '👉 please input database host, default (127.0.0.1):') ? : '127.0.0.1'; + $port = $this->output->ask($this->input, '👉 please input database host port, default (3306):') ? : '3306'; + $prefix = $this->output->ask($this->input, '👉 please input table prefix, default (null):') ? : ''; + $username = $this->output->ask($this->input, '👉 please input database username default (root): ') ? : 'root'; + $password = ''; + while (!$password) { + $password = $this->output->ask($this->input, '👉 please input database password: '); + if ($password) { + break; + } + } + + $this->generateEnvFile($host, $database, $username, $password, $port, $charset, $prefix); + } + } + + /** + * 安装第二部 + * + * @time 2019年11月29日 + * @return void + */ + protected function secondStep(): void + { + $modulePaths = glob(root_path('module') . '*'); + + $this->checkRootDatabase(); + + foreach ($modulePaths as $path) { + if (is_dir($path)) { + $moduleDatabasePath = $path . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR; + if (is_dir($moduleDatabasePath)) { + if (is_dir($moduleDatabasePath . 'migrations' . DIRECTORY_SEPARATOR)) { + $migrationFiles = glob($moduleDatabasePath . 'migrations' . DIRECTORY_SEPARATOR . '*.php'); + foreach ($migrationFiles as $file) { + copy($file, + root_path('database') . 'migrations'. DIRECTORY_SEPARATOR . + pathinfo($file, PATHINFO_BASENAME)); + } + } + + if (is_dir($moduleDatabasePath . 'seeds' . DIRECTORY_SEPARATOR)) { + $seedFiles = glob($moduleDatabasePath . 'seeds' . DIRECTORY_SEPARATOR . '*.php'); + foreach ($seedFiles as $file) { + copy($file, + root_path('database') . 'seeds' . DIRECTORY_SEPARATOR . + pathinfo($file, PATHINFO_BASENAME)); + } + } + } + } + } + } + + /** + * 安装第四步 + * + * @time 2019年11月29日 + * @return void + */ + protected function thirdStep(): void + { + Console::call('catch:cache'); + } + + /** + * finally + * + * @time 2019年11月30日 + * @return void + */ + protected function finished(): void + { + // todo something + if ($this->dataInstall) { + rmdir($this->app->getRootPath() . 'database'); + } + } + + /** + * generate env file + * + * @time 2019年11月29日 + * @param $host + * @param $database + * @param $username + * @param $password + * @param $port + * @param $charset + * @param $prefix + * @return void + */ + protected function generateEnvFile($host, $database, $username, $password, $port, $charset, $prefix): void + { + $env = \parse_ini_file(root_path() . '.example.env', true); + + $env['DATABASE']['HOSTNAME'] = $host; + $env['DATABASE']['DATABASE'] = $database; + $env['DATABASE']['USERNAME'] = $username; + $env['DATABASE']['PASSWORD'] = $password; + $env['DATABASE']['HOSTPORT'] = $port; + $env['DATABASE']['CHARSET'] = $charset; + if ($prefix) { + $env['DATABASE']['PREFIX'] = $prefix; + } + $dotEnv = ''; + foreach ($env as $key => $e) { + if (is_string($e)) { + $dotEnv .= sprintf('%s = %s', $key, $e === '1' ? 'true' : ($e === '' ? 'false' : $e)) . PHP_EOL; + $dotEnv .= PHP_EOL; + } else { + $dotEnv .= sprintf('[%s]', $key) . PHP_EOL; + foreach ($e as $k => $v) { + $dotEnv .= sprintf('%s = %s', $k, $v === '1' ? 'true' : ($v === '' ? 'false' : $v)) . PHP_EOL; + } + + $dotEnv .= PHP_EOL; + } + } + + file_put_contents(root_path() . '.env', $dotEnv); + + if ($this->getEnvFile()) { + $this->output->info('env file has been generated'); + } + + if ((new \mysqli($host, $username, $password, null, $port))->query(sprintf('CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARSET %s COLLATE %s_general_ci;', + $database, $charset, $charset))) { + $this->output->info(sprintf('🎉 create database %s successfully', $database)); + + exec(sprintf('%s %s migrate:run', getenv('_'), root_path() . DIRECTORY_SEPARATOR . 'think')); + + $this->output->info('🎉 database table install successfully'); + + exec(sprintf('%s %s seed:run', getenv('_'),root_path() . DIRECTORY_SEPARATOR . 'think')); + + $this->output->info('🎉 Fill database table successfully '); + } else { + $this->dataInstall = false; + $this->output->warning(sprintf('create database %s failed, you should create it by yourself', $database)); + $this->output->warning('you should use `php think migrate:run` to create tables'); + $this->output->warning('you should use `php think seed:run` to fill tables data'); + } + } + + /** + * + * @time 2019年11月29日 + * @return string + */ + protected function getEnvFile(): string + { + return file_exists(root_path() . '.env') ? root_path() . '.env' : ''; + } + + /** + * 检测根目录 + * + * @time 2019年11月28日 + * @return bool + */ + protected function checkRootDatabase(): bool + { + $databasePath = root_path('database'); + + if (!is_dir($databasePath)) { + if (!mkdir($databasePath, 0777, true) && !is_dir($databasePath)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $databasePath)); + } + } + + $migrationPath = $databasePath . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR; + + $seedPath = $databasePath . DIRECTORY_SEPARATOR . 'seeds' . DIRECTORY_SEPARATOR; + + if (!is_dir($migrationPath)) { + if (!mkdir($migrationPath, 0777, true) && !is_dir($migrationPath)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $migrationPath)); + } + } + + if (!is_dir($seedPath)) { + if (!mkdir($seedPath, 0777, true) && !is_dir($seedPath)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $seedPath)); + } + } + + return true; + } + + + protected function project() + { + $year = date('Y'); + + $this->output->info('🎉 project is installed, welcome!'); + + $this->output->info(sprintf(' + /-------------------- welcome to use -------------------------\ +| __ __ ___ __ _ | +| _________ _/ /______/ /_ / | ____/ /___ ___ (_)___ | +| / ___/ __ `/ __/ ___/ __ \ / /| |/ __ / __ `__ \/ / __ \ | +| / /__/ /_/ / /_/ /__/ / / / / ___ / /_/ / / / / / / / / / / | +| \___/\__,_/\__/\___/_/ /_/ /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ | +| | + \ __ __ __ __ _ __ _ __ enjoy it ! _ __ __ __ __ __ __ ___ _ @ 2017 ~ %s + +', $year)); + + } +} diff --git a/extend/catcher/command/MigrateRunCommand.php b/extend/catcher/command/MigrateRunCommand.php new file mode 100644 index 0000000..ba808b8 --- /dev/null +++ b/extend/catcher/command/MigrateRunCommand.php @@ -0,0 +1,64 @@ +setName('catch-migrate:run') + ->setDescription('Migrate the database') + ->addArgument('module', Argument::REQUIRED, 'migrate the module database') + ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to migrate to') + ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to migrate to') + ->setHelp(<<migrate:run command runs all available migrations, optionally up to a specific version + +php think catch-migrate:run module +php think catch-migrate:run -t 20110103081132 +php think catch-migrate:run -d 20110103 +php think catch-migrate:run -v + +EOT + ); + } + + protected function execute(Input $input, Output $output) + { + $this->module = strtolower($input->getArgument('module')); + $version = $input->getOption('target'); + $date = $input->getOption('date'); + + // run the migrations + $start = microtime(true); + if (null !== $date) { + $this->migrateToDateTime(new \DateTime($date)); + } else { + $this->migrate($version); + } + $end = microtime(true); + + $output->writeln(''); + $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . ''); + } + + /** + * 获取 migration path + * + * @time 2019年12月03日 + * @param $module + * @return string + */ + protected function getPath() + { + return CatchAdmin::moduleMigrationsDirectory($this->module); + } +} \ No newline at end of file diff --git a/extend/catcher/command/ModelGeneratorCommand.php b/extend/catcher/command/ModelGeneratorCommand.php new file mode 100644 index 0000000..66e68e1 --- /dev/null +++ b/extend/catcher/command/ModelGeneratorCommand.php @@ -0,0 +1,95 @@ +setName('create:model') + ->addArgument('model', Argument::REQUIRED, 'model name') + ->addArgument('module', Argument::REQUIRED, 'module name') + ->setDescription('create model'); + } + + protected function execute(Input $input, Output $output) + { + $model = ucfirst($input->getArgument('model')); + $module = strtolower($input->getArgument('module')); + + $table = Str::snake($model); + + $modelFile= CatchAdmin::getModuleModelDirectory($module) . $model . '.php'; + + file_put_contents($modelFile, $this->replaceContent([ + $module, $model, $table, $this->generateFields($this->getTableFields($table)) + ])); + + if (file_exists($modelFile)) { + $output->info(sprintf('%s Create Successfully!', $modelFile)); + } else { + $output->error(sprintf('%s Create Failed!', $modelFile)); + } + } + + + + private function getTableFields($table): array + { + $fields = Db::query('show full columns from ' . + config('database.connections.mysql.prefix') . $table); + + $new = []; + + foreach ($fields as $field) { + $new[$field['Field']] = $field['Comment']; + } + + return $new; + } + + private function generateFields($fields) + { + $f = ''; + foreach ($fields as $field => $comment) { + $f .= sprintf("'%s', // %s" . "\r\n\t\t\t", $field, $comment); + } + + return $f; + } + + private function replaceContent(array $replace) + { + return str_replace([ + '{Module}', '{Class}', '{Name}', '{Field}' + ], $replace, $this->content()); + } + + private function content() + { + return <<setName('catch:cache') + ->setDescription('cache routes, views, services of module'); + } + + protected function execute(Input $input, Output $output) + { + CatchAdmin::cacheRoutes(); + CatchAdmin::cacheServices(); + CatchAdmin::cacheViews(); + // 指令输出 + $output->info('succeed!'); + } +} diff --git a/extend/catcher/event/LoadModuleRoutes.php b/extend/catcher/event/LoadModuleRoutes.php new file mode 100644 index 0000000..864ab7b --- /dev/null +++ b/extend/catcher/event/LoadModuleRoutes.php @@ -0,0 +1,25 @@ +group(function () use ($router) { + include CatchAdmin::getRoutes(); + }); + } +} diff --git a/extend/catcher/exceptions/LoginFailedException.php b/extend/catcher/exceptions/LoginFailedException.php new file mode 100644 index 0000000..1470ca7 --- /dev/null +++ b/extend/catcher/exceptions/LoginFailedException.php @@ -0,0 +1,7 @@ + $value) { + if (in_array($field, $this->field)) { + $this->{$field} = $value; + } + } + + if ($this->save()) { + return $this->id; + } + + return false; + } + + /** + * + * @time 2019年12月03日 + * @param $id + * @param $data + * @return bool + */ + public function updateBy($id, $data) + { + $model = $this->findBy($id); + foreach ($data as $field => $value) { + if (in_array($field, $this->field)) { + $model->{$field} = $value; + } + } + + if ($model->save()) { + $model->id; + } + + return false; + } + + /** + * + * @time 2019年12月03日 + * @param $id + * @param array $field + * @return mixed + */ + public function findBy($id, array $field = ['*']) + { + return static::where($this->getPk(), $id)->select($field)->find(); + } + + /** + * + * @time 2019年12月03日 + * @param $id + * @return mixed + */ + public function deleteBy($id) + { + return static::destory($id); + } +} diff --git a/extend/catcher/traits/db/TransTrait.php b/extend/catcher/traits/db/TransTrait.php new file mode 100644 index 0000000..ec51f5f --- /dev/null +++ b/extend/catcher/traits/db/TransTrait.php @@ -0,0 +1,48 @@ +