diff --git a/composer.json b/composer.json index 5889a16d..b0ea5499 100644 --- a/composer.json +++ b/composer.json @@ -10,8 +10,8 @@ } ], "require": { - "php": ">=7.0", - "laravel/framework": ">=5.2.0", + "php": ">=7.1", + "laravel/framework": ">=5.4.0", "guzzlehttp/guzzle": "~6.0", "tymon/jwt-auth": "0.5.* || 1.0.*", "minime/annotations": "~3.0", diff --git a/src/BaseRequest.php b/src/BaseRequest.php index 6668a9bd..317136fc 100644 --- a/src/BaseRequest.php +++ b/src/BaseRequest.php @@ -4,17 +4,19 @@ use Illuminate\Support\Arr; use Illuminate\Foundation\Http\FormRequest; -use RonasIT\Support\AutoDoc\Traits\AutoDocRequestTrait; class BaseRequest extends FormRequest { - use AutoDocRequestTrait; - - public function authorize() + public function authorize(): bool { return true; } + public function rules(): array + { + return []; + } + /** * @param array|string $keys * @param mixed $default @@ -25,7 +27,7 @@ public function authorize() * @return array; */ - public function onlyValidated($keys = null, $default = null) + public function onlyValidated($keys = null, $default = null): array { $rules = array_keys($this->rules()); @@ -40,7 +42,7 @@ public function onlyValidated($keys = null, $default = null) return $validatedFields; } - protected function filterOnlyValidated($fields, $validation) + protected function filterOnlyValidated($fields, $validation): array { $result = []; @@ -72,12 +74,12 @@ protected function processNestedRule($validatedKeys, $validatedItem) }, $validatedItem); } - protected function isNotNestedRule($validatedKeys) + protected function isNotNestedRule($validatedKeys): bool { return is_integer($validatedKeys); } - private function sortByStrlen(array &$array) + private function sortByStrlen(array &$array): void { $collection = collect($array)->sortBy(function ($string) { return strlen($string); diff --git a/src/Exceptions/CircularRelationsFoundedException.php b/src/Exceptions/CircularRelationsFoundedException.php index fedd74fc..2118dabe 100644 --- a/src/Exceptions/CircularRelationsFoundedException.php +++ b/src/Exceptions/CircularRelationsFoundedException.php @@ -4,5 +4,4 @@ class CircularRelationsFoundedException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/ClassAlreadyExistsException.php b/src/Exceptions/ClassAlreadyExistsException.php index b648d86d..3babeebc 100644 --- a/src/Exceptions/ClassAlreadyExistsException.php +++ b/src/Exceptions/ClassAlreadyExistsException.php @@ -4,5 +4,4 @@ class ClassAlreadyExistsException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/ClassNotExistsException.php b/src/Exceptions/ClassNotExistsException.php index ee64ac14..52981f93 100644 --- a/src/Exceptions/ClassNotExistsException.php +++ b/src/Exceptions/ClassNotExistsException.php @@ -4,5 +4,4 @@ class ClassNotExistsException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/EntityCreateException.php b/src/Exceptions/EntityCreateException.php index 990a8d45..f419667a 100644 --- a/src/Exceptions/EntityCreateException.php +++ b/src/Exceptions/EntityCreateException.php @@ -6,5 +6,4 @@ class EntityCreateException extends Exception { - -} \ No newline at end of file +} diff --git a/src/Exceptions/IncorrectCSVFileException.php b/src/Exceptions/IncorrectCSVFileException.php index dbc5b454..7f4c32b1 100644 --- a/src/Exceptions/IncorrectCSVFileException.php +++ b/src/Exceptions/IncorrectCSVFileException.php @@ -4,5 +4,4 @@ class IncorrectCSVFileException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/IncorrectImportFileException.php b/src/Exceptions/IncorrectImportFileException.php index 184c8671..d3386acd 100644 --- a/src/Exceptions/IncorrectImportFileException.php +++ b/src/Exceptions/IncorrectImportFileException.php @@ -6,5 +6,4 @@ class IncorrectImportFileException extends Exception { - -} \ No newline at end of file +} diff --git a/src/Exceptions/IncorrectImportLineException.php b/src/Exceptions/IncorrectImportLineException.php index f3ddc4e5..eb4676f5 100644 --- a/src/Exceptions/IncorrectImportLineException.php +++ b/src/Exceptions/IncorrectImportLineException.php @@ -6,5 +6,4 @@ class IncorrectImportLineException extends Exception { - -} \ No newline at end of file +} diff --git a/src/Exceptions/InvalidModelException.php b/src/Exceptions/InvalidModelException.php index a92bae2e..1d49af81 100644 --- a/src/Exceptions/InvalidModelException.php +++ b/src/Exceptions/InvalidModelException.php @@ -6,5 +6,4 @@ class InvalidModelException extends Exception { - -} \ No newline at end of file +} diff --git a/src/Exceptions/ModelFactoryNotFound.php b/src/Exceptions/ModelFactoryNotFound.php index 3685a90d..0a12411f 100644 --- a/src/Exceptions/ModelFactoryNotFound.php +++ b/src/Exceptions/ModelFactoryNotFound.php @@ -4,5 +4,4 @@ class ModelFactoryNotFound extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/ModelFactoryNotFoundedException.php b/src/Exceptions/ModelFactoryNotFoundedException.php index 96b9eb71..9cdb5f44 100644 --- a/src/Exceptions/ModelFactoryNotFoundedException.php +++ b/src/Exceptions/ModelFactoryNotFoundedException.php @@ -4,5 +4,4 @@ class ModelFactoryNotFoundedException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exceptions/UnknownRequestMethodException.php b/src/Exceptions/UnknownRequestMethodException.php index e57e8a09..3508e696 100644 --- a/src/Exceptions/UnknownRequestMethodException.php +++ b/src/Exceptions/UnknownRequestMethodException.php @@ -4,5 +4,4 @@ class UnknownRequestMethodException extends EntityCreateException { - -} \ No newline at end of file +} diff --git a/src/Exporters/Exporter.php b/src/Exporters/Exporter.php index 47c448a1..68d8945e 100644 --- a/src/Exporters/Exporter.php +++ b/src/Exporters/Exporter.php @@ -3,6 +3,7 @@ namespace RonasIT\Support\Exporters; use Illuminate\Support\Arr; +use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\Storage; use Maatwebsite\Excel\Concerns\FromQuery; use Maatwebsite\Excel\Concerns\Exportable; @@ -31,12 +32,14 @@ public function setQuery($query): self return $this; } - public function setDisk($disk) + public function setDisk($disk): self { $this->disk = $disk; + + return $this; } - public function setFileName($fileName): self + public function setFileName(string $fileName): self { $this->fileName = $fileName; @@ -48,7 +51,7 @@ public function setFileName($fileName): self * * @return $this */ - public function setType($type): self + public function setType(string $type): self { $this->type = $type; @@ -64,7 +67,7 @@ public function export(): string return Storage::disk($this->disk)->path($filename); } - public function query() + public function query(): Builder { return $this->query; } diff --git a/src/Importers/Importer.php b/src/Importers/Importer.php index 1410e92b..0d6fd261 100644 --- a/src/Importers/Importer.php +++ b/src/Importers/Importer.php @@ -33,28 +33,28 @@ class Importer ]; protected $mandatoryFields = []; - public function setInput($input) + public function setInput($input): self { $this->input = $input; return $this; } - public function setService($service) + public function setService($service): self { $this->service = $service; return $this; } - public function setExporter($exporter) + public function setExporter($exporter): self { $this->exporter = app($exporter); return $this; } - public function import() + public function import(): array { $this->prepare(); @@ -65,12 +65,14 @@ public function import() return $this->report; } - public function prepare() + public function prepare(): self { $this->iterator = new CsvIterator($this->input); + + return $this; } - protected function markAllLines() + protected function markAllLines(): void { $isFilterLine = true; @@ -102,13 +104,13 @@ protected function markAllLines() } } - protected function resolve() + protected function resolve(): void { $this->createAllMarked(); $this->updateAllMarked(); } - protected function prepareFields($line) + protected function prepareFields(array $line): array { return array_map(function ($field) { $field = strtolower($field); @@ -117,7 +119,7 @@ protected function prepareFields($line) }, $line); } - protected function prepareLine($line) + protected function prepareLine(array $line): array { if (empty($line['id'])) { $line['id'] = null; @@ -130,7 +132,7 @@ protected function prepareLine($line) return $line; } - protected function markForCreate($line) + protected function markForCreate(array $line): void { if (!$this->isValidForCreation($line)) { return; @@ -139,7 +141,7 @@ protected function markForCreate($line) $this->items[self::ITEMS_TO_CREATE][] = $line; } - protected function isValidForCreation($line) + protected function isValidForCreation(array $line): bool { if (empty($line['id'])) { return true; @@ -148,14 +150,14 @@ protected function isValidForCreation($line) return !$this->service->withTrashed()->exists(['id' => $line['id']]); } - protected function markForUpdate($line) + protected function markForUpdate(array $line): void { if ($this->isValidForUpdating($line)) { $this->items[self::ITEMS_TO_UPDATE][$line['id']] = $line; } } - protected function isValidForUpdating($line) + protected function isValidForUpdating(array $line): bool { if (empty($line['id']) || in_array($line, $this->items[self::ITEMS_TO_UPDATE])) { return false; @@ -166,19 +168,19 @@ protected function isValidForUpdating($line) return !empty($diff); } - protected function addError($error) + protected function addError(string $error): void { $this->report['errors'][] = $this->formatError($error); } - protected function formatError($error) + protected function formatError(string $error): string { $lineNumber = $this->iterator->key() + 1; return "Line {$lineNumber}: {$error}"; } - protected function validateDuplicatingOfId($item) + protected function validateDuplicatingOfId(array $item): bool { if (empty($item['id'])) { return false; @@ -189,7 +191,7 @@ protected function validateDuplicatingOfId($item) ]); } - protected function createAllMarked() + protected function createAllMarked(): void { foreach ($this->items[self::ITEMS_TO_CREATE] as $item) { $this->service->create($item); @@ -198,7 +200,7 @@ protected function createAllMarked() } } - protected function updateAllMarked() + protected function updateAllMarked(): void { foreach ($this->items[self::ITEMS_TO_UPDATE] as $id => $item) { $this->service->update(['id' => $id], $item); @@ -207,7 +209,7 @@ protected function updateAllMarked() } } - protected function getDiff($item) + protected function getDiff(array $item): array { $itemFromDB = $this->service->first(['id' => $item['id']]); @@ -217,7 +219,7 @@ protected function getDiff($item) return array_diff($exportedLine, $importedLine); } - protected function validateHeader() + protected function validateHeader(): void { $line = $this->iterator->current(); @@ -237,4 +239,4 @@ protected function validateHeader() throw new IncorrectImportFileException($message); } } -} \ No newline at end of file +} diff --git a/src/Interfaces/DataCollectorInterface.php b/src/Interfaces/DataCollectorInterface.php deleted file mode 100755 index 58b982b9..00000000 --- a/src/Interfaces/DataCollectorInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -filePath = $filePath; } - public function parseColumns($columns) + public function parseColumns($columns): self { $this->columns = $columns; return $this; } - public function rewind() + public function rewind(): void { fclose($this->file); @@ -54,7 +55,7 @@ public function current() }); } - public function getGenerator() + public function getGenerator(): Generator { $this->rewind(); @@ -72,14 +73,14 @@ public function key() return $this->currentRow; } - public function next() + public function next(): void { $this->currentCsvLine = fgetcsv($this->file); $this->currentRow++; } - public function valid() + public function valid(): bool { return $this->currentCsvLine !== false; } -} \ No newline at end of file +} diff --git a/src/Iterators/DBIterator.php b/src/Iterators/DBIterator.php index eddb9366..ab910574 100644 --- a/src/Iterators/DBIterator.php +++ b/src/Iterators/DBIterator.php @@ -3,6 +3,7 @@ namespace RonasIT\Support\Iterators; use Iterator; +use Generator; class DBIterator implements Iterator { @@ -18,7 +19,7 @@ public function __construct($query, $itemsPerPage = 100) $this->itemsPerPage = $itemsPerPage; } - public function rewind() + public function rewind(): void { $this->loadSample(); } @@ -33,7 +34,7 @@ public function key() return $this->sample['data'][$this->position]['id']; } - public function next() + public function next(): void { $this->position++; @@ -42,22 +43,22 @@ public function next() } } - public function valid() + public function valid(): bool { return !($this->isLastSample() && $this->isEndOfSample()); } - public function isEndOfSample() + public function isEndOfSample(): bool { return $this->position >= count($this->sample['data']); } - public function isLastSample() + public function isLastSample(): bool { return $this->sample['current_page'] >= $this->sample['last_page']; } - public function loadSample($page = 1) + public function loadSample($page = 1): void { $this->sample = $this->query ->paginate($this->itemsPerPage, ['*'], 'page', $page) @@ -66,7 +67,7 @@ public function loadSample($page = 1) $this->position = 0; } - public function getGenerator() + public function getGenerator(): Generator { $this->rewind(); @@ -76,4 +77,4 @@ public function getGenerator() $this->next(); } } -} \ No newline at end of file +} diff --git a/src/Middleware/AuthWithRefresh.php b/src/Middleware/AuthWithRefresh.php index 12030adf..f51c564b 100644 --- a/src/Middleware/AuthWithRefresh.php +++ b/src/Middleware/AuthWithRefresh.php @@ -3,6 +3,7 @@ namespace RonasIT\Support\Middleware; use Closure; +use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenExpiredException; @@ -13,8 +14,8 @@ class AuthWithRefresh extends GetUserFromToken /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param Request $request + * @param Closure $next * @return mixed * @throws JWTException * @throws JWTException @@ -50,12 +51,12 @@ private function authenticate($request, $next) /** * Refresh token * - * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param Request $request + * @param Closure $next * @return mixed * @throws JWTException */ - private function refreshToken($request, Closure $next) + private function refreshToken(Request $request, Closure $next) { try { $newToken = $this->auth->setRequest($request)->parseToken()->refresh(); @@ -69,4 +70,4 @@ private function refreshToken($request, Closure $next) return $response; } -} \ No newline at end of file +} diff --git a/src/Middleware/SecurityMiddleware.php b/src/Middleware/SecurityMiddleware.php index 904e580c..f2a654a5 100644 --- a/src/Middleware/SecurityMiddleware.php +++ b/src/Middleware/SecurityMiddleware.php @@ -33,19 +33,19 @@ public function handle($request, Closure $next) return $next($request); } - protected function needToLock($request) + protected function needToLock($request): bool { return ( - ($request->header('Order66') == 'activate') && - ($request->header('App-Key') == config('app.key')) + ($request->header('Order66') === 'activate') && + ($request->header('App-Key') === config('app.key')) ); } - protected function needToUnlock($request) + protected function needToUnlock($request): bool { return ( - ($request->header('Order66') == 'deactivate') && - ($request->header('App-Key') == config('app.key')) + ($request->header('Order66') === 'deactivate') && + ($request->header('App-Key') === config('app.key')) ); } @@ -56,4 +56,4 @@ protected function getFailResponse() return response(view("errors.{$code}")->render(), $code); } -} \ No newline at end of file +} diff --git a/src/Middleware/UndemandingAuthorizationMiddleware.php b/src/Middleware/UndemandingAuthorizationMiddleware.php deleted file mode 100644 index 8cff18f2..00000000 --- a/src/Middleware/UndemandingAuthorizationMiddleware.php +++ /dev/null @@ -1,67 +0,0 @@ -auth = app(JWTAuth::class); - $this->events = app(Dispatcher::class); - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - */ - public function handle($request, Closure $next) - { - try { - return $this->authenticate($request, $next); - } catch (TokenExpiredException $e) { - return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]); - } catch (JWTException $e) { - return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]); - } - } - - private function authenticate($request, $next) - { - if (!$token = $this->auth->setRequest($request)->getToken()) { - return $next($request); - } - - $user = $this->auth->authenticate($token); - - if (!$user) { - return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404); - } - - $this->events->fire('tymon.jwt.valid', $user); - - return $next($request); - } -} diff --git a/src/Repositories/BaseRepository.php b/src/Repositories/BaseRepository.php index 89bb7868..cd41dedf 100755 --- a/src/Repositories/BaseRepository.php +++ b/src/Repositories/BaseRepository.php @@ -7,18 +7,4 @@ class BaseRepository { use EntityControlTrait; - - protected $isImport = false; - - public function importMode($mode = true) - { - $this->isImport = $mode; - - return $this; - } - - public function isImportMode() - { - return $this->isImport; - } -} \ No newline at end of file +} diff --git a/src/Services/EntityService.php b/src/Services/EntityService.php index cf1adaaa..283a4fe8 100644 --- a/src/Services/EntityService.php +++ b/src/Services/EntityService.php @@ -11,7 +11,7 @@ class EntityService { protected $repository; - public function setRepository($repository) + public function setRepository($repository): self { $this->repository = app($repository); diff --git a/src/Services/HttpRequestService.php b/src/Services/HttpRequestService.php index 72b66df3..f42cbe7d 100755 --- a/src/Services/HttpRequestService.php +++ b/src/Services/HttpRequestService.php @@ -5,6 +5,7 @@ use GuzzleHttp\Client; use Illuminate\Support\Arr; use GuzzleHttp\Cookie\CookieJar; +use Psr\Http\Message\ResponseInterface; use GuzzleHttp\Exception\RequestException; use RonasIT\Support\Exceptions\UnknownRequestMethodException; @@ -18,33 +19,40 @@ class HttpRequestService protected $options = []; protected $cookies = null; + protected $response; + public function __construct() { $this->debug = config('defaults.http_service_debug', false); } - public function set($key, $value) + public function set(string $key, $value): self { $this->options[$key] = $value; return $this; } - public function parseJsonResponse($response) + public function json(): array { - $stringResponse = (string)$response->getBody(); + $stringResponse = (string) $this->response->getBody(); return json_decode($stringResponse, true); } - public function saveCookieSession() + public function getResponse(): ResponseInterface + { + return $this->response; + } + + public function saveCookieSession(): self { $this->cookies = app(CookieJar::class); return $this; } - public function getCookie() + public function getCookie(): array { if (empty($this->cookies)) { return []; @@ -53,46 +61,46 @@ public function getCookie() return $this->cookies->toArray(); } - public function allowRedirects($value = true) + public function allowRedirects(bool $value = true): self { $this->allowRedirects = $value; return $this; } - public function setConnectTimeout($seconds = 0) + public function setConnectTimeout(int $seconds = 0): self { $this->connectTimeout = $seconds; return $this; } - public function sendGet($url, $data = null, $headers = []) + public function get(string $url, array $data = [], array $headers = []): self { return $this->send('get', $url, $data, $headers); } - public function sendPost($url, $data, $headers = []) + public function post(string $url, array $data, array $headers = []): self { return $this->send('post', $url, $data, $headers); } - public function sendDelete($url, $headers = []) + public function delete(string $url, array $headers = []): self { return $this->send('delete', $url, [], $headers); } - public function sendPut($url, $data, $headers = []) + public function put(string $url, array $data, array $headers = []): self { return $this->send('put', $url, $data, $headers); } - public function sendPatch($url, $data, $headers = []) + public function patch(string $url, array $data, array $headers = []): self { return $this->send('patch', $url, $data, $headers); } - protected function send($method, $url, $data = [], $headers = []) + protected function send(string $method, string $url, array $data = [], array $headers = []): self { $time = microtime(true); @@ -101,37 +109,37 @@ protected function send($method, $url, $data = [], $headers = []) $this->setData($method, $headers, $data); try { - $response = $this->sendRequest($method, $url); + $this->response = $this->sendRequest($method, $url); - return $response; + return $this; } catch (RequestException $exception) { - $response = $exception->getResponse(); + $this->response = $exception->getResponse(); throw $exception; } finally { - $this->logResponse($response, $time); + $this->logResponse($this->response, $time); $this->options = []; } } - protected function sendRequest($method, $url) + protected function sendRequest($method, $url): ResponseInterface { $client = new Client(); switch ($method) { - case 'get' : + case 'get': $response = $client->get($url, $this->options); break; - case 'post' : + case 'post': $response = $client->post($url, $this->options); break; - case 'put' : + case 'put': $response = $client->put($url, $this->options); break; - case 'patch' : + case 'patch': $response = $client->patch($url, $this->options); break; - case 'delete' : + case 'delete': $response = $client->delete($url, $this->options); break; default : @@ -141,7 +149,7 @@ protected function sendRequest($method, $url) return $response; } - protected function logRequest($typeOfRequest, $url, $data, $headers) + protected function logRequest(string $typeOfRequest, string $url, array $data, array $headers): void { if ($this->debug) { logger(''); @@ -156,7 +164,7 @@ protected function logRequest($typeOfRequest, $url, $data, $headers) } } - protected function logResponse($response, $time = null) + protected function logResponse(ResponseInterface $response, ?int $time = null): void { if ($this->debug) { logger(''); @@ -170,15 +178,17 @@ protected function logResponse($response, $time = null) } } - private function setOptions($headers) + private function setOptions(array $headers): self { $this->options['headers'] = $headers; $this->options['cookies'] = $this->cookies; $this->options['allow_redirects'] = $this->allowRedirects; $this->options['connect_timeout'] = $this->connectTimeout; + + return $this; } - private function setData($method, $headers, $data = []) + private function setData(string $method, array $headers, array $data = []): void { if (empty($data)) { return; diff --git a/src/Stubs/add_default_user.blade.php b/src/Stubs/add_default_user.blade.php deleted file mode 100755 index 6374a811..00000000 --- a/src/Stubs/add_default_user.blade.php +++ /dev/null @@ -1,28 +0,0 @@ -use App\Models\User; -use Illuminate\Database\Migrations\Migration; -use RonasIT\Support\Traits\MigrationTrait; -use App\Repositories\RoleRepository; - -class AddDefaultUser extends Migration -{ - use MigrationTrait; - - public function up() - { - if (config('app.env') != 'testing') { - User::create([ - 'name' => '{{$name}}', - 'email' => '{{$email}}', - 'password' => bcrypt('{{$password}}'), - 'role_id' => RoleRepository::ADMIN_ROLE - ]); - } - } - - public function down() - { - if (config('app.env') != 'testing') { - User::where('email', '{{$email}}')->delete(); - } - } -} \ No newline at end of file diff --git a/src/Traits/EntityControlTrait.php b/src/Traits/EntityControlTrait.php index 657e5e3b..ef831923 100755 --- a/src/Traits/EntityControlTrait.php +++ b/src/Traits/EntityControlTrait.php @@ -2,11 +2,13 @@ namespace RonasIT\Support\Traits; +use Closure; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Builder as Query; use RonasIT\Support\Exceptions\InvalidModelException; -use RonasIT\Support\Exceptions\PostValidationException; /** * @property Model model @@ -15,27 +17,29 @@ trait EntityControlTrait { use SearchTrait; - protected $requiredRelations = []; - protected $requiredRelationsCount = []; protected $model; - protected $withTrashed = false; - protected $onlyTrashed = false; protected $fields; protected $primaryKey; - protected $forceMode; + + protected $withTrashed = false; + protected $onlyTrashed = false; + protected $forceMode = false; + protected $visibleAttributes = []; protected $hiddenAttributes = []; - public function all() + public function all(): Collection { return $this->get(); } - public function truncate() + public function truncate(): self { $modelInstance = $this->model; $modelInstance::truncate(); + + return $this; } public function force($value = true): self @@ -45,7 +49,7 @@ public function force($value = true): self return $this; } - public function setModel($modelClass) + public function setModel($modelClass): self { $this->model = new $modelClass(); @@ -54,10 +58,13 @@ public function setModel($modelClass) $this->primaryKey = $this->model->getKeyName(); $this->checkPrimaryKey(); + + return $this; } /** * @param array|string $hiddenAttributes + * * @return $this */ public function makeHidden($hiddenAttributes = []): self @@ -69,6 +76,7 @@ public function makeHidden($hiddenAttributes = []): self /** * @param array|string $visibleAttributes + * * @return $this */ public function makeVisible($visibleAttributes = []): self @@ -78,7 +86,7 @@ public function makeVisible($visibleAttributes = []): self return $this; } - protected function getQuery($where = []) + protected function getQuery($where = []): Query { $query = $this->model->query(); @@ -88,19 +96,17 @@ protected function getQuery($where = []) $this->withTrashed = false; } - if ($this->withTrashed && $this->isSoftDelete()) { + if ($this->withTrashed && $this->hasSoftDeleteTrait()) { $query->withTrashed(); } - if (!empty($this->requiredRelations)) { - $query->with($this->requiredRelations); + if (!empty($this->attachedRelations)) { + $query->with($this->attachedRelations); } - if (!empty($this->requiredRelationsCount)) { - foreach ($this->requiredRelationsCount as $requestedRelations) { - $explodedRelation = explode('.', $requestedRelations); - $countRelation = array_pop($explodedRelation); - $relation = implode('.', $explodedRelation); + if (!empty($this->attachedRelationsCount)) { + foreach ($this->attachedRelationsCount as $requestedRelations) { + list ($countRelation, $relation) = extract_last_part($requestedRelations); if (empty($relation)) { $query->withCount($countRelation); @@ -117,30 +123,6 @@ protected function getQuery($where = []) return $this->constructWhere($query, $where); } - /** - * @param $relations array|string - * - * @return $this - */ - public function withRelations($relations): self - { - $this->requiredRelations = Arr::wrap($relations); - - return $this; - } - - /** - * @param $relations array|string - * - * @return $this - */ - public function withRelationsCount($relations): self - { - $this->requiredRelationsCount = Arr::wrap($relations); - - return $this; - } - /** * Check entity existing in database. * @@ -161,12 +143,12 @@ public function exists($where): bool * * @return boolean */ - public function existsBy($field, $value): bool + public function existsBy(string $field, $value): bool { return $this->getQuery([$field => $value])->exists(); } - public function create($data) + public function create(array $data): Model { $entityData = Arr::only($data, $this->fields); $modelClass = get_class($this->model); @@ -183,45 +165,30 @@ public function create($data) $this->afterCreateHook($model, $data); - if (!empty($this->requiredRelations)) { - $model->load($this->requiredRelations); + if (!empty($this->attachedRelations)) { + $model->load($this->attachedRelations); } return $model ->makeHidden($this->hiddenAttributes) - ->makeVisible($this->visibleAttributes) - ->toArray(); + ->makeVisible($this->visibleAttributes); } /** * Update rows by condition or primary key * - * @param array|integer $where + * @param mixed $where * @param array $data - * @param bool $updatedRecordsAsResult - * @param int $limit * * @return array|int */ - public function updateMany($where, array $data, bool $updatedRecordsAsResult = true, int $limit = 50) + public function updateMany($where, array $data): int { $modelClass = get_class($this->model); $fields = $this->forceMode ? $modelClass::getFields() : $this->model->getFillable(); $entityData = Arr::only($data, $fields); - $idsToUpdate = []; - - $this->chunk($limit, function ($items) use (&$idsToUpdate) { - $idsToUpdate = array_merge($idsToUpdate, Arr::pluck($items, 'id')); - }, $where); - - $updatedRowsCount = $this->updateByList($idsToUpdate, $entityData); - - if (!$updatedRecordsAsResult) { - return $updatedRowsCount; - } - - return $this->getByList($unUpdatedIds); + return $this->getQuery($where)->update($entityData); } /** @@ -230,14 +197,14 @@ public function updateMany($where, array $data, bool $updatedRecordsAsResult = t * @param array|integer $where * @param array $data * - * @return array + * @return Model */ - public function update($where, array $data) + public function update($where, array $data): ?Model { $item = $this->getQuery($where)->first(); if (empty($item)) { - return []; + return null; } if ($this->forceMode) { @@ -251,17 +218,16 @@ public function update($where, array $data) $this->afterUpdateHook($item, $data); - if (!empty($this->requiredRelations)) { - $item->load($this->requiredRelations); + if (!empty($this->attachedRelations)) { + $item->load($this->attachedRelations); } return $item ->makeHidden($this->hiddenAttributes) - ->makeVisible($this->visibleAttributes) - ->toArray(); + ->makeVisible($this->visibleAttributes); } - public function updateOrCreate($where, $data) + public function updateOrCreate($where, $data): Model { if ($this->exists($where)) { return $this->update($where, $data); @@ -274,61 +240,46 @@ public function updateOrCreate($where, $data) return $this->create(array_merge($data, $where)); } - public function count($where = []) + public function count($where = []): int { return $this->getQuery($where)->count(); } - public function get(array $where = []) + public function get(array $where = []): Collection { $result = $this->getQuery($where)->get(); $this->applyHidingShowingFieldsRules($result); - return $result->toArray(); - } - - /** - * @deprecated - */ - public function getOrCreate($data) - { - $entities = $this->get($data); - - if (empty($entities)) { - return $this->create($data); - } - - return $entities; + return $result; } - public function first($where = []) + public function first($where = []): ?Model { $entity = $this->getQuery($where)->first(); - return empty($entity) ? [] : $entity + return empty($entity) ? null : $entity ->makeHidden($this->hiddenAttributes) - ->makeVisible($this->visibleAttributes) - ->toArray(); + ->makeVisible($this->visibleAttributes); } - public function findBy(string $field, $value) + public function findBy(string $field, $value): ?Model { return $this->first([$field => $value]); } - public function find($id) + public function find($id): ?Model { - return $this->first([$this->primaryKey => $id]); + return $this->first($id); } /** * @param array|string|int $where array of conditions or primary key value * @param array $data * - * @return array|mixed + * @return Model */ - public function firstOrCreate($where, array $data = []) + public function firstOrCreate($where, array $data = []): Model { $entity = $this->first($where); @@ -343,9 +294,10 @@ public function firstOrCreate($where, array $data = []) * Delete rows by condition or primary key * * @param array|integer|string $where + * * @return integer count of deleted rows */ - public function delete($where) + public function delete($where): int { $query = $this->getQuery($where); @@ -356,14 +308,6 @@ public function delete($where) } } - /** - * @deprecated - */ - public function forceDelete($where) - { - $this->getQuery($where)->forceDelete(); - } - public function withTrashed($enable = true): self { $this->withTrashed = $enable; @@ -378,12 +322,12 @@ public function onlyTrashed($enable = true): self return $this; } - public function restore($where) + public function restore($where): int { return $this->getQuery($where)->onlyTrashed()->restore(); } - public function chunk($limit, $callback, $where = []) + public function chunk(int $limit, Closure $callback, array $where = []): void { $this ->getQuery($where) @@ -391,7 +335,7 @@ public function chunk($limit, $callback, $where = []) ->chunk($limit, function ($items) use ($callback) { $this->applyHidingShowingFieldsRules($items); - $callback($items->toArray()); + $callback($items); }); } @@ -399,10 +343,11 @@ public function chunk($limit, $callback, $where = []) * Delete rows by list of values a particular field or primary key * * @param array $values - * @param string|null $field condition field, primary key is default value + * @param ?string $field condition field, primary key is default value + * * @return integer count of deleted rows */ - public function deleteByList(array $values, $field = null) + public function deleteByList(array $values, ?string $field = null): int { $field = (empty($field)) ? $this->primaryKey : $field; @@ -410,32 +355,25 @@ public function deleteByList(array $values, $field = null) ->getQuery() ->whereIn($field, $values); - if ($this->forceMode && $this->isSoftDelete()) { + if ($this->forceMode && $this->hasSoftDeleteTrait()) { return $query->forceDelete(); } else { return $query->delete(); } } - public function restoreByList($values, $field = null) + public function restoreByList(array $values, ?string $field = null): int { $field = (empty($field)) ? $this->primaryKey : $field; - $query = $this + return $this ->getQuery() ->onlyTrashed() - ->whereIn($field, $values); - - $entities = $query->get(); - - $this->applyHidingShowingFieldsRules($entities); - - $query->restore(); - - return $entities->toArray(); + ->whereIn($field, $values) + ->restore(); } - public function getByList(array $values, $field = null) + public function getByList(array $values, ?string $field = null): Collection { $field = (empty($field)) ? $this->primaryKey : $field; @@ -446,17 +384,17 @@ public function getByList(array $values, $field = null) $this->applyHidingShowingFieldsRules($result); - return $result->toArray(); + return $result; } - public function countByList(array $values, $field = null): int + public function countByList(array $values, ?string $field = null): int { $field = (empty($field)) ? $this->primaryKey : $field; return $this->getQuery()->whereIn($field, $values)->count(); } - public function updateByList(array $values, $data, $field = null) + public function updateByList(array $values, array $data, $field = null): int { $field = (empty($field)) ? $this->primaryKey : $field; @@ -467,21 +405,21 @@ public function updateByList(array $values, $data, $field = null) return $query->update(Arr::only($data, $fields)); } - protected function getEntityName() + protected function getEntityName(): string { $explodedModel = explode('\\', get_class($this->model)); return end($explodedModel); } - protected function isSoftDelete(): bool + protected function hasSoftDeleteTrait(): bool { $traits = class_uses(get_class($this->model)); return in_array(SoftDeletes::class, $traits); } - protected function checkPrimaryKey() + protected function checkPrimaryKey(): void { if (is_null($this->primaryKey)) { $modelClass = get_class($this->model); @@ -490,12 +428,12 @@ protected function checkPrimaryKey() } } - protected function afterUpdateHook($entity, $data) + protected function afterUpdateHook(?Model $entity, array $data) { // implement it yourself if you need it } - protected function afterCreateHook($entity, $data) + protected function afterCreateHook(?Model $entity, array $data) { // implement it yourself if you need it } diff --git a/src/Traits/FilesTestTrait.php b/src/Traits/FilesTestTrait.php deleted file mode 100644 index 2f870c0c..00000000 --- a/src/Traits/FilesTestTrait.php +++ /dev/null @@ -1,44 +0,0 @@ -assertEquals( - $this->getFixture($fixturePath), - Storage::get($uploadedFileName) - ); - } - - public function clearFolder() - { - $files = Storage::allFiles(); - - if (!empty($files)) { - $this->deleteFiles($files); - } - } - - public function getFilePathFromUrl($url) - { - $explodedUrl = explode('/', $url); - - return last($explodedUrl); - } - - protected function deleteFiles($files) - { - foreach ($files as $file) { - Storage::delete($file); - } - } -} \ No newline at end of file diff --git a/src/Traits/FilesTrait.php b/src/Traits/FilesTrait.php deleted file mode 100644 index 5690ad03..00000000 --- a/src/Traits/FilesTrait.php +++ /dev/null @@ -1,50 +0,0 @@ -prepareName($name); - - Storage::put($preparedName, $content); - - return $returnUrl ? Storage::url($preparedName) : Storage::path($preparedName); - } - - public function removeFileByUrl($url) - { - $fileName = $this->getFileNameFromUrl($url); - - $this->removeFileByName($fileName); - } - - public function removeFileByName($name) - { - Storage::delete($name); - } - - protected function prepareName($name) - { - $explodedName = explode('.', $name); - $extension = array_pop($explodedName); - $timestamp = Carbon::now()->timestamp; - $hash = md5($name); - - return "{$hash}_{$timestamp}.{$extension}"; - } - - protected function getFileNameFromUrl($url) - { - $explodedUrl = explode('/', $url); - - return last($explodedUrl); - } -} \ No newline at end of file diff --git a/src/Traits/FixturesTrait.php b/src/Traits/FixturesTrait.php index 046ccdcc..a9492c9d 100644 --- a/src/Traits/FixturesTrait.php +++ b/src/Traits/FixturesTrait.php @@ -50,7 +50,7 @@ trait FixturesTrait protected $truncateExceptTables = ['migrations', 'password_resets']; protected $prepareSequencesExceptTables = ['migrations', 'password_resets', 'settings']; - protected function loadTestDump() + protected function loadTestDump(): void { $dump = $this->getFixture('dump.sql', false); @@ -66,18 +66,18 @@ protected function loadTestDump() DB::unprepared($dump); } - public function getFixturePath($fn) + public function getFixturePath(string $fixtureName): string { $class = get_class($this); $explodedClass = explode('\\', $class); $className = Arr::last($explodedClass); - return base_path("tests/fixtures/{$className}/{$fn}"); + return base_path("tests/fixtures/{$className}/{$fixtureName}"); } - public function getFixture($fn, $failIfNotExists = true) + public function getFixture(string $fixtureName, $failIfNotExists = true): string { - $path = $this->getFixturePath($fn); + $path = $this->getFixturePath($fixtureName); if (file_exists($path)) { return file_get_contents($path); @@ -90,19 +90,12 @@ public function getFixture($fn, $failIfNotExists = true) return ''; } - public function getJsonFixture($fn, $assoc = true) + public function getJsonFixture(string $fixtureName, $assoc = true) { - return json_decode($this->getFixture($fn), $assoc); + return json_decode($this->getFixture($fixtureName), $assoc); } - public function getJsonResponse() - { - $response = $this->response->getContent(); - - return json_decode($response, true); - } - - public function assertEqualsFixture($fixture, $data, bool $exportMode = false) + public function assertEqualsFixture(string $fixture, $data, bool $exportMode = false): void { if ($exportMode) { $this->exportJson($fixture, $data); @@ -111,27 +104,14 @@ public function assertEqualsFixture($fixture, $data, bool $exportMode = false) $this->assertEquals($this->getJsonFixture($fixture), $data); } - /** - * This method is actual only for Laravel 5.3 and lower - * - * @deprecated - */ - public function exportJsonResponse($fixture) - { - $response = $this->getJsonResponse(); - $content = json_encode($response, JSON_PRETTY_PRINT); - - return file_put_contents($this->getFixturePath($fixture), $content); - } - - public function callRawRequest($method, $uri, $content, array $headers = []) + public function callRawRequest(string $method, string $uri, $content, array $headers = []): TestResponse { $server = $this->transformHeadersToServerVars($headers); return $this->call($method, $uri, [], [], [], $server, $content); } - public function exportJson($fixture, $data) + public function exportJson($fixture, $data): void { if ($data instanceof TestResponse) { $data = $data->json(); @@ -140,7 +120,7 @@ public function exportJson($fixture, $data) $this->exportContent(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), $fixture); } - public function clearDatabase($scheme, $tables, $except) + public function clearDatabase(string $scheme, array $tables, array $except): void { if ($scheme === 'pgsql') { $query = $this->getClearPsqlDatabaseQuery($tables, $except); @@ -153,7 +133,7 @@ public function clearDatabase($scheme, $tables, $except) } } - public function getClearPsqlDatabaseQuery($tables, $except = ['migrations']) + public function getClearPsqlDatabaseQuery(array $tables, array $except = ['migrations']): string { return array_concat($tables, function ($table) use ($except) { if (in_array($table, $except)) { @@ -164,7 +144,7 @@ public function getClearPsqlDatabaseQuery($tables, $except = ['migrations']) }); } - public function getClearMySQLDatabaseQuery($tables, $except = ['migrations']) + public function getClearMySQLDatabaseQuery(array $tables, array $except = ['migrations']): string { $query = "SET FOREIGN_KEY_CHECKS = 0;\n"; @@ -179,7 +159,7 @@ public function getClearMySQLDatabaseQuery($tables, $except = ['migrations']) return "{$query} SET FOREIGN_KEY_CHECKS = 1;\n"; } - public function prepareSequences($tables, $except = []) + public function prepareSequences(array $tables, array $except = []): void { $except = array_merge($this->postgisTables, $this->prepareSequencesExceptTables, $except); @@ -194,7 +174,7 @@ public function prepareSequences($tables, $except = []) app('db.connection')->unprepared($query); } - public function exportFile($response, $fixture) + public function exportFile(TestResponse $response, string $fixture): void { $this->exportContent( file_get_contents($response->getFile()->getPathName()), @@ -202,7 +182,7 @@ public function exportFile($response, $fixture) ); } - protected function getTables() + protected function getTables(): array { if (empty(self::$tables)) { self::$tables = app('db.connection') @@ -213,7 +193,7 @@ protected function getTables() return self::$tables; } - protected function exportContent($content, $fixture) + protected function exportContent($content, string $fixture): void { if (env('FAIL_EXPORT_JSON', true)) { $this->fail(preg_replace('/[ ]+/mu', ' ', diff --git a/src/Traits/ImageTestTrait.php b/src/Traits/ImageTestTrait.php deleted file mode 100644 index 64ee2967..00000000 --- a/src/Traits/ImageTestTrait.php +++ /dev/null @@ -1,72 +0,0 @@ -uploadPath($folder); - - if (!file_exists($imagesPath)) { - mkdir_recursively($imagesPath); - } - - $images = scandir($imagesPath); - - array_map(function ($image) use ($folder) { - $except = ['.', '..']; - - if (!in_array($image, $except)) { - $imagePath = $this->uploadPath("{$folder}/{$image}"); - - unlink($imagePath); - } - }, $images); - } - - protected function getEncryptedImage($imageName) - { - $image = $this->getFixture($imageName); - - return base64_encode($image); - } - - protected function checkImage($expectedFixture, $imagePath) - { - $imagePath = preg_replace('/^\//', '', $imagePath); - - $imagePath = $this->uploadPath($imagePath); - - $this->assertEquals( - $this->getFixture($expectedFixture), - file_get_contents($imagePath) - ); - } - - protected function copyImage($imageName, $destination) - { - if (!file_exists($this->uploadPath('flags'))) { - mkdir_recursively($this->uploadPath('flags')); - } - - copy( - $this->getFixturePath($imageName), - $this->uploadPath($destination) - ); - } - - protected function getImageRoute($imagePath) - { - $route = config('imagecache.route'); - $templates = config('imagecache.templates'); - $templatesKeys = array_keys($templates); - - return "/{$route}/{$templatesKeys[0]}/{$imagePath}"; - } -} \ No newline at end of file diff --git a/src/Traits/ImagesTrait.php b/src/Traits/ImagesTrait.php deleted file mode 100644 index ab1ee494..00000000 --- a/src/Traits/ImagesTrait.php +++ /dev/null @@ -1,58 +0,0 @@ -prepareImageFolder($folder); - - $imagePath = "/{$folder}/{$name}"; - - $image = base64_decode($image); - - file_put_contents($this->uploadPath($imagePath), $image); - - return $imagePath; - } - - protected function getUploadFolder() - { - if (env('APP_ENV') == 'testing') { - $dir = config('defaults.upload.test'); - - if (!file_exists($dir)) { - mkdir($dir); - } - - return $dir; - } - - return config('defaults.upload.prod'); - } - - protected function uploadPath($folder) - { - return "{$this->getUploadFolder()}/{$folder}"; - } - - protected function prepareImageFolder($folder) - { - $folderPath = $this->uploadPath($folder); - - mkdir_recursively($folderPath); - } - - protected function removeImage($imagePath) - { - $imagePath = $this->uploadPath($imagePath); - - if (file_exists($imagePath)) { - unlink($imagePath); - } - } -} \ No newline at end of file diff --git a/src/Traits/MockClassTrait.php b/src/Traits/MockClassTrait.php new file mode 100644 index 00000000..cf819abc --- /dev/null +++ b/src/Traits/MockClassTrait.php @@ -0,0 +1,36 @@ + 'yourMethod', + * 'result' => 'result_fixture.json' + * ] + * ] + * + * @param string $class + * @param array $callChain + */ + public function mockClass(string $class, array $callChain): void + { + $methods = Arr::pluck($callChain, 'method'); + $mock = $this + ->getMockBuilder($class) + ->setMethods($methods) + ->getMock(); + + foreach ($callChain as $call) { + $mock->method($call['method'])->willReturn($call['result']); + } + + $this->app->instance($class, $mock); + } +} diff --git a/src/Traits/MockHttpRequestTrait.php b/src/Traits/MockHttpRequestTrait.php deleted file mode 100644 index f5cab881..00000000 --- a/src/Traits/MockHttpRequestTrait.php +++ /dev/null @@ -1,57 +0,0 @@ -makeGetRequest($this->makeResponse($response), $http); - } - - public function makeGetRequest($response, $http = null) - { - if (empty($http)) { - $http = Mockery::mock(HttpRequestService::class); - } - - $http->shouldReceive('sendGet')->once()->andReturn($response); - - return $http; - } - - public function makePostRequest($response, $http = null) - { - if (empty($http)) { - $http = Mockery::mock(HttpRequestService::class); - } - - $http->shouldReceive('sendPost')->once()->andReturn($response); - - return $http; - } - - public function makeResponse($body = null) - { - $httpResponse = Mockery::mock(ClientInterface::class); - $httpResponse->shouldReceive('isSuccessful')->andReturn(true); - $httpResponse->shouldReceive('getBody')->andReturn($body); - - return $httpResponse; - } - - public function mockMethod($method, $result, $http) - { - if (empty($http)) { - $http = Mockery::mock(HttpRequestService::class); - } - - $http->shouldReceive($method)->once()->andReturn($result); - - return $http; - } -} \ No newline at end of file diff --git a/src/Traits/ModelTrait.php b/src/Traits/ModelTrait.php index 7b72ad7c..c634ca7b 100644 --- a/src/Traits/ModelTrait.php +++ b/src/Traits/ModelTrait.php @@ -12,22 +12,9 @@ trait ModelTrait { - protected static $forceVisible = []; - protected static $forceHidden = []; + protected $disableLazyLoading = true; - protected $disableLazyLoading = false; - - public static function setForceVisibleFields($fields) - { - self::$forceVisible = $fields; - } - - public static function setForceHiddenFields($fields) - { - self::$forceHidden = $fields; - } - - public static function getFields() + public static function getFields(): array { $model = (new static); @@ -41,15 +28,7 @@ public static function getFields() return array_merge($fillable, $guarded, $timeStamps); } - public function toArray() - { - $hidden = array_merge($this->hidden, self::$forceHidden); - $this->setHidden(array_subtraction($hidden, self::$forceVisible)); - - return parent::toArray(); - } - - public function getAllFieldsWithTable() + public function getAllFieldsWithTable(): array { $fields = Schema::getColumnListing($this->getTable()); @@ -72,13 +51,14 @@ protected function getRelationshipFromMethod($method) } /** - * This method was added, because native laravel's method addSelect + * This method was added, because native Laravel's method addSelect * overwrites existed select clause * @param $query * @param $fields + * * @return mixed */ - public function scopeAddFieldsToSelect($query, $fields = null) + public function scopeAddFieldsToSelect($query, array $fields = []) { if (is_null($query->getQuery()->columns)) { $query->addSelect("{$this->getTable()}.*"); @@ -103,7 +83,7 @@ public function scopeAddFieldsToSelect($query, $fields = null) * * @return QueryBuilder */ - public function scopeOrderByRelated($query, $relations, $desc = 'DESC', $asField = null, $manyToManyStrategy = 'max') + public function scopeOrderByRelated(QueryBuilder $query, string $relations, string $desc = 'DESC', ?string $asField = null, string $manyToManyStrategy = 'max'): QueryBuilder { if (version_compare(app()::VERSION, '5.5') <= 0) { return $query->legacyOrderByRelated($relations, $desc); @@ -121,11 +101,13 @@ public function scopeOrderByRelated($query, $relations, $desc = 'DESC', $asField $prevQuery = array_shift($queries); array_pop($queries); - $this->applyManyToManyStrategy($prevQuery, $manyToManyStrategy) + $this + ->applyManyToManyStrategy($prevQuery, $manyToManyStrategy) ->select($orderField); foreach ($queries as $queryInCollection) { - $prevQuery = $this->applyManyToManyStrategy($queryInCollection, $manyToManyStrategy) + $prevQuery = $this + ->applyManyToManyStrategy($queryInCollection, $manyToManyStrategy) ->selectSub($prevQuery, $asField); } @@ -143,7 +125,7 @@ protected function getRelationWithoutConstraints($query, $relation) }); } - protected function prepareRelations($relations) + protected function prepareRelations(string $relations): array { if (Str::contains($relations, '.')) { return explode('.', $relations); @@ -154,7 +136,7 @@ protected function prepareRelations($relations) } } - private function getOrderedField(&$relations) + private function getOrderedField(&$relations): string { if (is_array($relations)) { return array_pop($relations); @@ -163,12 +145,10 @@ private function getOrderedField(&$relations) return $relations; } - protected function getQueriesList($query, $relations) + protected function getQueriesList(QueryBuilder $query, array $relations): array { $requiredColumns = []; - $queryCollection = [ - $query - ]; + $queryCollection = [$query]; foreach ($relations as $relationString) { $query = Arr::last($queryCollection); @@ -186,7 +166,7 @@ protected function getQueriesList($query, $relations) return array_reverse($queryCollection); } - protected function applyManyToManyStrategy($query, $strategy) + protected function applyManyToManyStrategy(QueryBuilder $query, string $strategy): QueryBuilder { if ($strategy === 'max') { $query->orderBy('id', 'ASC')->limit(1); @@ -201,12 +181,9 @@ protected function applyManyToManyStrategy($query, $strategy) * Unfortunately, Laravel older than 5.5 does not support Relation::noConstraints so for such versions we * have to use simplified version of orderByRelated which does not support nesting relations. */ - public function scopeLegacyOrderByRelated($query, $orderField, $desc = 'DESC') + public function scopeLegacyOrderByRelated(QueryBuilder $query, string $orderField, string $desc = 'DESC'): void { - $entities = explode('.', $orderField); - - $fieldName = array_pop($entities); - $relationName = array_shift($entities); + list ($fieldName, $relationName) = extract_last_part($orderField); if (Str::plural($relationName) !== $relationName) { $table = $this->getTable(); @@ -222,5 +199,4 @@ public function scopeLegacyOrderByRelated($query, $orderField, $desc = 'DESC') ->orderBy('orderedField', $desc); } } - } diff --git a/src/Traits/SearchTrait.php b/src/Traits/SearchTrait.php index 72a8f252..b3dd908a 100644 --- a/src/Traits/SearchTrait.php +++ b/src/Traits/SearchTrait.php @@ -2,6 +2,8 @@ namespace RonasIT\Support\Traits; +use Closure; +use Illuminate\Support\Collection; use Illuminate\Foundation\Application; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\Paginator; @@ -18,7 +20,10 @@ trait SearchTrait protected $query; protected $filter; - public function paginate() + protected $attachedRelations = []; + protected $attachedRelationsCount = []; + + public function paginate(): LengthAwarePaginator { $defaultPerPage = config('defaults.items_per_page'); $perPage = Arr::get($this->filter, 'per_page', $defaultPerPage); @@ -28,16 +33,16 @@ public function paginate() } /** - * @param $field string, filtered field, you can pass field name with dots to filter by field of relation - * @param $filterName string|null, key from filters which contains filter value - * @return $this + * @param $field string filtered field, you can pass field name with dots to filter by field of relation + * @param $filterName string|null key from filters which contains filter value + * + * @return self */ - public function filterBy($field, $filterName = null) + public function filterBy(string $field, ?string $filterName = null): self { if (empty($filterName)) { if (Str::contains($field, '.')) { - $entities = explode('.', $field); - $filterName = Arr::last($entities); + list ($filterName) = extract_last_part($field); } else { $filterName = $field; } @@ -50,15 +55,13 @@ public function filterBy($field, $filterName = null) return $this; } - public function filterByQuery(array $fields) + public function filterByQuery(array $fields): self { if (!empty($this->filter['query'])) { $this->query->where(function ($query) use ($fields) { foreach ($fields as $field) { if (Str::contains($field, '.')) { - $entities = explode('.', $field); - $fieldName = array_pop($entities); - $relations = implode('.', $entities); + list ($fieldName, $relations) = extract_last_part($field); $query->orWhereHas($relations, function ($query) use ($fieldName) { $query->where( @@ -77,7 +80,7 @@ public function filterByQuery(array $fields) return $this; } - public function searchQuery($filter) + public function searchQuery(array $filter): self { if (!empty($filter['with_trashed'])) { $this->withTrashed(); @@ -90,12 +93,12 @@ public function searchQuery($filter) return $this; } - public function getSearchResults() + public function getSearchResults(): LengthAwarePaginator { $this->orderBy(); if (empty($this->filter['all'])) { - return $this->getModifiedPaginator($this->paginate())->toArray(); + return $this->getModifiedPaginator($this->paginate()); } $data = $this->query->get(); @@ -103,9 +106,10 @@ public function getSearchResults() return $this->wrapPaginatedData($data); } - public function wrapPaginatedData($data) + public function wrapPaginatedData(Collection $data): LengthAwarePaginator { - $total = count($data); + $total = $data->count(); + $perPage = $this->calculatePerPage($total); $paginator = new LengthAwarePaginator($data, count($data), $perPage, 1, [ @@ -113,10 +117,10 @@ public function wrapPaginatedData($data) 'pageName' => 'page' ]); - return $this->getModifiedPaginator($paginator)->toArray(); + return $this->getModifiedPaginator($paginator); } - public function getModifiedPaginator($paginator) + public function getModifiedPaginator(LengthAwarePaginator $paginator): LengthAwarePaginator { $collection = $paginator->getCollection(); @@ -125,7 +129,7 @@ public function getModifiedPaginator($paginator) return $paginator->setCollection($collection); } - public function orderBy($default = null, $defaultDesc = false) + public function orderBy(?string $default = null, bool $defaultDesc = false): self { $default = (empty($default)) ? $this->primaryKey : $default; @@ -138,61 +142,39 @@ public function orderBy($default = null, $defaultDesc = false) $this->query->orderBy($orderField, $this->getDesc($isDesc)); } - if ($orderField != $default) { + if ($orderField !== $default) { $this->query->orderBy($default, $this->getDesc($defaultDesc)); } return $this; } - protected function getDesc($isDesc) + protected function getDesc(bool $isDesc): string { return $isDesc ? 'DESC' : 'ASC'; } - /** - * @deprecated - * - * Use filterBy() with dot notation instead - */ - public function filterByRelationField($relation, $field, $filterName = null) - { - if (empty($filterName)) { - $filterName = $field; - } - - if (Arr::has($this->filter, $filterName)) { - $this->query->whereHas($relation, function ($query) use ($field, $filterName) { - $query->where( - $field, $this->filter[$filterName] - ); - }); - } - - return $this; - } - - public function filterMoreThan($field, $value) + public function filterMoreThan(string $field, $value): self { return $this->filterValue($field, '>', $value); } - public function filterLessThan($field, $value) + public function filterLessThan(string $field, $value): self { return $this->filterValue($field, '<', $value); } - public function filterMoreOrEqualThan($field, $value) + public function filterMoreOrEqualThan(string $field, $value): self { return $this->filterValue($field, '>=', $value); } - public function filterLessOrEqualThan($field, $value) + public function filterLessOrEqualThan(string $field, $value): self { return $this->filterValue($field, '<=', $value); } - public function filterValue($field, $sign, $value) + public function filterValue(string $field, string $sign, $value): self { if (!empty($value)) { $this->query->where($field, $sign, $value); @@ -201,16 +183,31 @@ public function filterValue($field, $sign, $value) return $this; } - public function with() + /** + * @param $relations array|string + * + * @return $this + */ + public function with($relations): self { - if (!empty($this->filter['with'])) { - $this->query->with($this->filter['with']); - } + $this->attachedRelations = Arr::wrap($relations); + + return $this; + } + + /** + * @param $relations array|string + * + * @return $this + */ + public function withCount($relations): self + { + $this->attachedRelationsCount = Arr::wrap($relations); return $this; } - protected function getQuerySearchCallback($field) + protected function getQuerySearchCallback(string $field): Closure { $databaseDriver = config('database.default'); $dbRawValue = "lower({$field})"; @@ -227,7 +224,7 @@ protected function getQuerySearchCallback($field) }; } - public function filterByList($field, $filterName) + public function filterByList(string $field, string $filterName): self { if (Arr::has($this->filter, $filterName)) { $this->applyWhereCallback($this->query, $field, function (&$q, $conditionField) use ($filterName) { @@ -238,7 +235,7 @@ public function filterByList($field, $filterName) return $this; } - public function filterFrom($field, $strict = true, $filterName = null) + public function filterFrom(string $field, bool $strict = true, ?string $filterName = null): self { $filterName = empty($filterName) ? 'from' : $filterName; $sign = $strict ? '>' : '>='; @@ -250,7 +247,7 @@ public function filterFrom($field, $strict = true, $filterName = null) return $this; } - public function filterTo($field, $strict = true, $filterName = null) + public function filterTo(string $field, bool $strict = true, ?string $filterName = null): self { $filterName = empty($filterName) ? 'to' : $filterName; $sign = $strict ? '<' : '<='; @@ -262,42 +259,19 @@ public function filterTo($field, $strict = true, $filterName = null) return $this; } - public function withCount() - { - if (!empty($this->filter['with_count'])) { - foreach ($this->filter['with_count'] as $requestedRelations) { - $explodedRelation = explode('.', $requestedRelations); - $countRelation = array_pop($explodedRelation); - $relation = implode('.', $explodedRelation); - - if (empty($relation)) { - $this->query->withCount($countRelation); - } else { - $this->query->with([ - $relation => function ($query) use ($countRelation) { - $query->withCount($countRelation); - } - ]); - } - } - } - - return $this; - } - - public function getSearchQuery() + public function getSearchQuery(): Query { return $this->query; } - protected function addWhere(&$query, $field, $value, $sign = '=') + protected function addWhere(Query &$query, string $field, $value, string $sign = '='): void { $this->applyWhereCallback($query, $field, function (&$q, $field) use ($sign, $value) { $q->where($field, $sign, $value); }); } - protected function constructWhere($query, $where = [], $field = null) + protected function constructWhere(Query $query, $where = [], ?string $field = null): Query { if (!is_array($where)) { $field = (empty($field)) ? $this->primaryKey : $field; @@ -314,11 +288,10 @@ protected function constructWhere($query, $where = [], $field = null) return $query; } - protected function applyWhereCallback($query, $field, $callback) { + protected function applyWhereCallback(Query $query, string $field, Closure $callback): void + { if (Str::contains($field, '.')) { - $entities = explode('.', $field); - $conditionField = array_pop($entities); - $relations = implode('.', $entities); + list ($conditionField, $relations) = extract_last_part($field); $query->whereHas($relations, function ($q) use ($callback, $conditionField) { $callback($q, $conditionField); @@ -328,7 +301,7 @@ protected function applyWhereCallback($query, $field, $callback) { } } - protected function calculatePerPage($total) + protected function calculatePerPage(int $total): int { if ($total > 0) { return $total; @@ -341,12 +314,12 @@ protected function calculatePerPage($total) return config('defaults.items_per_page', 1); } - protected function applyHidingShowingFieldsRules(&$collection) + protected function applyHidingShowingFieldsRules(Collection &$collection): void { if (Application::VERSION >= '5.8') { $collection->makeHidden($this->hiddenAttributes)->makeVisible($this->visibleAttributes); } else { - $collection = $collection->each(function (&$item) { + $collection = $collection->each(function ($item) { $item->makeHidden($this->hiddenAttributes)->makeVisible($this->visibleAttributes); }); } diff --git a/src/helpers.php b/src/helpers.php index dd22d15a..611eccea 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -382,3 +382,14 @@ function array_undot($array) return $result; } + +function extract_last_part(string $string, string $separator = '.'): array +{ + $entities = explode($separator, $string); + + $fieldName = array_pop($entities); + + $relation = implode($separator, $entities); + + return [$fieldName, $relation]; +} diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php index 00de6ed5..5df6448e 100644 --- a/tests/HelpersTest.php +++ b/tests/HelpersTest.php @@ -8,7 +8,7 @@ class HelpersTest extends HelpersTestCase { use FixturesTrait; - public function getData() + public function getData(): array { return [ [ @@ -52,18 +52,16 @@ public function getData() /** * @dataProvider getData * - * @param array $input + * @param string $input * @param string $key - * @param array $expected + * @param string $expected */ - public function testGetList($input, $key, $expected) + public function testGetList(string $input, string $key, string $expected) { $input = $this->getJsonFixture($input); $result = array_get_list($input, $key); - $this->assertEquals( - $this->getJsonFixture($expected), $result - ); + $this->assertEqualsFixture($expected, $result); } -} \ No newline at end of file +} diff --git a/tests/HelpersTestCase.php b/tests/HelpersTestCase.php index d7df950c..75c625c6 100755 --- a/tests/HelpersTestCase.php +++ b/tests/HelpersTestCase.php @@ -2,8 +2,9 @@ namespace RonasIT\Support\Tests; -use Illuminate\Foundation\Testing\TestCase as BaseTest; +use Illuminate\Foundation\Application; use RonasIT\Support\Traits\FixturesTrait; +use Illuminate\Foundation\Testing\TestCase as BaseTest; class HelpersTestCase extends BaseTest { @@ -19,9 +20,9 @@ class HelpersTestCase extends BaseTest /** * Creates the application. * - * @return \Illuminate\Foundation\Application + * @return Application */ - public function createApplication() + public function createApplication(): Application { return require __DIR__ . '/../bootstrap/app.php'; } diff --git a/tests/TestCase.php b/tests/TestCase.php index 507f2f6a..647b946f 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,16 +3,17 @@ namespace RonasIT\Support\Tests; use Closure; +use Carbon\Carbon; use Tymon\JWTAuth\JWTAuth; use Illuminate\Support\Arr; +use Illuminate\Mail\Mailable; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Mail; +use Illuminate\Testing\TestResponse; use RonasIT\Support\Traits\FixturesTrait; +use Symfony\Component\HttpFoundation\Response; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Foundation\Testing\TestCase as BaseTest; -use Carbon\Carbon; -use Symfony\Component\HttpFoundation\Response; - abstract class TestCase extends BaseTest { @@ -57,14 +58,14 @@ public function setUp(): void $this->beginDatabaseTransaction(); } - public function actingAs(Authenticatable $user, $driver = null) + public function actingAs(Authenticatable $user, $driver = null): self { $this->jwt = $this->auth->fromUser($user); return $this; } - public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) + public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null): TestResponse { if (!empty($this->jwt)) { $server['HTTP_AUTHORIZATION'] = "Bearer {$this->jwt}"; @@ -118,8 +119,7 @@ public function tearDown(): void * @param mixed $emailChain * @param mixed $exportMode */ - - protected function assertMailEquals(string $mailableClass, $emailChain, $exportMode = false) + protected function assertMailEquals(string $mailableClass, array $emailChain, bool $exportMode = false): void { $emailChain = $this->prepareEmailChain($emailChain); $index = 0; @@ -129,7 +129,7 @@ protected function assertMailEquals(string $mailableClass, $emailChain, $exportM $this->assertCountMails($emailChain, $index); } - protected function assertSentCallback($emailChain, &$index, $exportMode): Closure + protected function assertSentCallback(array $emailChain, int &$index, bool $exportMode = false): Closure { return function ($mail) use ($emailChain, &$index, $exportMode) { $expectedMailData = Arr::get($emailChain, $index); @@ -145,7 +145,7 @@ protected function assertSentCallback($emailChain, &$index, $exportMode): Closur }; } - protected function validateExpectationParameters($currentMail, $index): void + protected function validateExpectationParameters(array $currentMail, int $index): void { foreach ($this->requiredExpectationParameters as $parameter) { if (!Arr::has($currentMail, $parameter)) { @@ -154,7 +154,7 @@ protected function validateExpectationParameters($currentMail, $index): void } } - protected function assertSubject($currentMail, $mail): void + protected function assertSubject(array $currentMail, Mailable $mail): void { $expectedSubject = Arr::get($currentMail, 'subject'); @@ -163,7 +163,7 @@ protected function assertSubject($currentMail, $mail): void } } - protected function assertAddressesCount(array $emails, $mail, int $index): void + protected function assertAddressesCount(array $emails, Mailable $mail, int $index): void { $expectedAddressesCount = count($emails); $addressesCount = count($mail->to); @@ -180,14 +180,14 @@ protected function assertSentToEmailsList(array $sentEmails, array $emails, int } } - protected function assertCountMails($emailChain, int $index): void + protected function assertCountMails(array $emailChain, int $index): void { $countData = count($emailChain); $this->assertEquals($countData, $index, "Failed assert that send emails count are equals, expected send email count: {$countData}, actual {$index}"); } - protected function assertEmailsList($expectedMailData, $mail, int $index): void + protected function assertEmailsList(array $expectedMailData, Mailable $mail, int $index): void { $sentEmails = Arr::pluck($mail->to, 'address'); $emails = Arr::wrap($expectedMailData['emails']); @@ -195,7 +195,7 @@ protected function assertEmailsList($expectedMailData, $mail, int $index): void $this->assertSentToEmailsList($sentEmails, $emails, $index); } - protected function assertFixture($expectedMailData, $mail, $exportMode): void + protected function assertFixture(array $expectedMailData, Mailable $mail, bool $exportMode = false): void { $mailContent = view($mail->view, $mail->getData())->render(); @@ -219,14 +219,14 @@ protected function prepareEmailChain($emailChain): array return is_array(Arr::first($emailChain)) ? $emailChain : [$emailChain]; } - protected function dontWrapIntoTransaction() + protected function dontWrapIntoTransaction(): void { $this->rollbackTransaction(); self::$isWrappedIntoTransaction = false; } - protected function beginDatabaseTransaction() + protected function beginDatabaseTransaction(): void { $database = $this->app->make('db'); @@ -244,12 +244,12 @@ protected function beginDatabaseTransaction() }); } - protected function connectionsToTransact() + protected function connectionsToTransact(): array { return property_exists($this, 'connectionsToTransact') ? $this->connectionsToTransact : [null]; } - protected function rollbackTransaction() + protected function rollbackTransaction(): void { $database = $this->app->make('db');