From a3097f0dc6f2563203d400b63eebe1aed73247e3 Mon Sep 17 00:00:00 2001 From: Mh-Asmi Date: Tue, 19 Nov 2024 09:09:27 +0400 Subject: [PATCH 1/5] only parameter for category --- .../FetchAllCategoriesQueryHandler.php | 32 ++-- .../FetchCategoryByIdQueryHandler.php | 16 +- .../Queries/FetchAllCategoriesQuery.php | 39 ++--- .../Queries/FetchCategoryByIdQuery.php | 19 +++ .../Modules/V5/DTO/CategorySearchFields.php | 19 +++ .../Http/Controllers/CategoryController.php | 47 +++--- src/Ushahidi/Modules/V5/Models/Category.php | 29 ++++ .../Category/CategoryRepository.php | 18 ++- .../Category/EloquentCategoryRepository.php | 146 +++++++++++------- tests/Integration/v5/tags.v5.feature | 6 +- 10 files changed, 242 insertions(+), 129 deletions(-) diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchAllCategoriesQueryHandler.php b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchAllCategoriesQueryHandler.php index d1a19fb5f3..64223810ad 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchAllCategoriesQueryHandler.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchAllCategoriesQueryHandler.php @@ -5,10 +5,8 @@ use App\Bus\Action; use App\Bus\Query\AbstractQueryHandler; use App\Bus\Query\Query; -use Ushahidi\Core\Tool\SearchData; use Ushahidi\Modules\V5\Actions\Category\Queries\FetchAllCategoriesQuery; use Ushahidi\Modules\V5\Repository\Category\CategoryRepository; -use Illuminate\Support\Facades\Auth; class FetchAllCategoriesQueryHandler extends AbstractQueryHandler { @@ -25,30 +23,18 @@ protected function isSupported(Query $query) } } - public function __invoke(Action $action) + public function __invoke(Action $query) { /** * @var FetchAllCategoriesQuery $action */ - $this->isSupported($action); - - $data = new SearchData; - - $searchFields = $action->getCategorySearchFields(); - - $user = Auth::guard()->user(); - - $data->setFilter('keyword', $searchFields->q()); - - $data->setFilter('tag', $searchFields->tag()); - $data->setFilter('type', $searchFields->type()); - $data->setFilter('role', $searchFields->role()); - $data->setFilter('user_id', $user->id ?? null); - $data->setFilter('parent_id', $searchFields->parentId()); - $data->setFilter('is_parent', $searchFields->level() === 'parent'); - $data->setFilter('is_admin', $searchFields->role() && $searchFields->role() == "admin"); - - $this->categoryRepository->setSearchParams($data); - return $this->categoryRepository->fetchAll($action->getPaging()); + $this->isSupported($query); + $only_fields = array_unique(array_merge($query->getFields(), $query->getFieldsForRelationship())); + return $this->categoryRepository->paginate( + $query->getPaging(), + $query->getSearchFields(), + $only_fields, + $query->getWithRelationship() + ); } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php index a9b0ab3c20..439eab2c94 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php @@ -6,11 +6,9 @@ use App\Bus\Action; use App\Bus\Query\AbstractQueryHandler; use App\Bus\Query\Query; -use Ushahidi\Core\Tool\SearchData; use Ushahidi\Modules\V5\Actions\Category\Queries\FetchCategoryByIdQuery; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\Repository\Category\CategoryRepository; -use Illuminate\Support\Facades\Auth; class FetchCategoryByIdQueryHandler extends AbstractQueryHandler { @@ -28,12 +26,20 @@ protected function isSupported(Query $query): void } } - public function __invoke(Action $action): Category + public function __invoke(Action $query): Category { /** * @var FetchCategoryByIdQuery $action */ - $this->isSupported($action); - return $this->categoryRepository->findById($action->getId()); + $this->isSupported($query); + return $this->categoryRepository->findById( + $query->getId(), + false, + array_unique(array_merge( + $query->getFields(), + $query->getFieldsForRelationship() + )), + $query->getWithRelationship() + ); } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchAllCategoriesQuery.php b/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchAllCategoriesQuery.php index 58b86ad66c..769d0a690d 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchAllCategoriesQuery.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchAllCategoriesQuery.php @@ -5,29 +5,30 @@ use App\Bus\Query\Query; use Ushahidi\Modules\V5\DTO\Paging; use Ushahidi\Modules\V5\DTO\CategorySearchFields; +use Ushahidi\Modules\V5\Models\Category; +use Illuminate\Http\Request; +use Ushahidi\Modules\V5\Traits\OnlyParameter\QueryWithOnlyParameter; +use Ushahidi\Modules\V5\Traits\HasPaginate; +use Ushahidi\Modules\V5\Traits\HasSearchFields; class FetchAllCategoriesQuery implements Query { - /** - * @var Paging - */ - private $paging; - private $category_search_fields; - - public function __construct(Paging $paging, CategorySearchFields $category_search_fields) - { - $this->paging = $paging; - $this->category_search_fields = $category_search_fields; - } - - public function getPaging(): Paging - { - return $this->paging; - } - - public function getCategorySearchFields() + use QueryWithOnlyParameter; + use HasPaginate; + use HasSearchFields; + + public static function fromRequest(Request $request): self { - return $this->category_search_fields; + $query = new self(); + $query->setPaging($request); + $query->setSearchFields(new CategorySearchFields($request)); + $query->addOnlyParameteresFromRequest( + $request, + Category::ALLOWED_FIELDS, + Category::ALLOWED_RELATIONSHIPS, + Category::REQUIRED_FIELDS + ); + return $query; } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchCategoryByIdQuery.php b/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchCategoryByIdQuery.php index 92bc9bdae3..a3848156a2 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchCategoryByIdQuery.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Queries/FetchCategoryByIdQuery.php @@ -3,9 +3,14 @@ namespace Ushahidi\Modules\V5\Actions\Category\Queries; use App\Bus\Query\Query; +use Ushahidi\Modules\V5\Models\Category; +use Illuminate\Http\Request; +use Ushahidi\Modules\V5\Traits\OnlyParameter\QueryWithOnlyParameter; class FetchCategoryByIdQuery implements Query { + use QueryWithOnlyParameter; + private $id; public function __construct(int $id) @@ -13,6 +18,20 @@ public function __construct(int $id) $this->id = $id; } + public static function fromRequest(int $id, Request $request): self + { + if ($id <= 0) { + throw new \InvalidArgumentException('Id must be a positive number'); + } + $query = new self($id); + $query->addOnlyParameteresFromRequest( + $request, + Category::ALLOWED_FIELDS, + Category::ALLOWED_RELATIONSHIPS, + Category::REQUIRED_FIELDS + ); + return $query; + } public function getId(): int { return $this->id; diff --git a/src/Ushahidi/Modules/V5/DTO/CategorySearchFields.php b/src/Ushahidi/Modules/V5/DTO/CategorySearchFields.php index 4b1cda1ccb..1079fdaf12 100644 --- a/src/Ushahidi/Modules/V5/DTO/CategorySearchFields.php +++ b/src/Ushahidi/Modules/V5/DTO/CategorySearchFields.php @@ -15,6 +15,10 @@ class CategorySearchFields extends SearchFields private $type; private $level; private $parent_id; + private $role; + private $user_id; + + public function __construct(Request $request) { $this->query = $request->query('q'); @@ -24,8 +28,10 @@ public function __construct(Request $request) $this->parent_id = $request->query('parent_id'); if (Auth::user()) { $this->role = Auth::user()->role; + $this->user_id = Auth::user()->id; } else { $this->role = null; + $this->user_id = null; } } @@ -58,4 +64,17 @@ public function role(): ?string { return $this->role; } + + public function isAdmin() + { + return ($this->role === "admin"); + } + public function userId() + { + return $this->user_id; + } + public function isParent() + { + return ($this->level === "parent"); + } } diff --git a/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php b/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php index 3fc370f14e..14f1a53e19 100644 --- a/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php +++ b/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php @@ -6,7 +6,6 @@ use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Foundation\Application; use Illuminate\Http\JsonResponse; -use Ushahidi\Modules\V5\DTO\Paging; use Symfony\Component\HttpFoundation\Response; use Ushahidi\Modules\V5\Actions\Category\Commands\DeleteCategoryCommand; use Ushahidi\Modules\V5\Actions\Category\Commands\StoreCategoryCommand; @@ -20,7 +19,6 @@ use Ushahidi\Modules\V5\Requests\CategoryRequest; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\Http\Requests\CategoryRequest as ValidationCategoryRequest; -use Ushahidi\Modules\V5\DTO\CategorySearchFields; class CategoryController extends V5Controller { @@ -31,12 +29,13 @@ class CategoryController extends V5Controller * @return mixed * @throws AuthorizationException */ - public function show(int $id): CategoryResource + public function show(Request $request, int $id): CategoryResource { - $category = $this->queryBus->handle(new FetchCategoryByIdQuery($id)); + $category = $this->queryBus->handle( + FetchCategoryByIdQuery::fromRequest($id, $request) + ); // $this->authorize('show', $category); - return new CategoryResource($category); } @@ -50,16 +49,30 @@ public function show(int $id): CategoryResource public function index(Request $request) { // $this->authorize('index', new Category()); - return new CategoryCollection( $this->queryBus->handle( - new FetchAllCategoriesQuery( - new Paging($request, 'id', Paging::ORDER_ASC, 0), - new CategorySearchFields($request) - ) + FetchAllCategoriesQuery::fromRequest($request) ) ); } + + private function getCategory(int $id, ?array $fields = null, ?array $haydrates = null) + { + if (!$fields) { + $fields = Category::ALLOWED_FIELDS; + } + if (!$haydrates) { + $haydrates = array_keys(Category::ALLOWED_RELATIONSHIPS); + } + $query = new FetchCategoryByIdQuery($id); + $query->addOnlyValues( + $fields, + $haydrates, + Category::ALLOWED_RELATIONSHIPS, + Category::REQUIRED_FIELDS + ); + return $this->queryBus->handle($query); + } /** * Display the specified resource. * @@ -73,7 +86,7 @@ public function store(CategoryRequest $request) $id = $this->commandBus->handle(StoreCategoryCommand::createFromRequest($request)); - $category = $this->queryBus->handle(new FetchCategoryByIdQuery($id)); + $category = $this->getCategory($id, Category::ALLOWED_FIELDS); DB::beginTransaction(); try { @@ -107,7 +120,8 @@ public function store(CategoryRequest $request) */ public function update(int $id, CategoryRequest $request) { - $category = $this->queryBus->handle(new FetchCategoryByIdQuery($id)); + $category = $this->getCategory($id, Category::ALLOWED_FIELDS, []); + $this->authorize('update', $category); DB::beginTransaction(); try { @@ -169,16 +183,9 @@ public function validateTranslations($category, $entity_array, array $translatio public function delete(int $id) { - // try { - $category = $this->queryBus->handle(new FetchCategoryByIdQuery($id)); + $category = $this->getCategory($id, ['id'], []); $this->authorize('delete', $category); - // $success = DB::transaction(function () use ($category) { - // $category->translations()->delete(); - // $success = $category->delete(); - // return $success; - // }); - $this->commandBus->handle(new DeleteCategoryCommand($id)); return $this->deleteResponse($id); } diff --git a/src/Ushahidi/Modules/V5/Models/Category.php b/src/Ushahidi/Modules/V5/Models/Category.php index 876643279f..7d0f36ff21 100644 --- a/src/Ushahidi/Modules/V5/Models/Category.php +++ b/src/Ushahidi/Modules/V5/Models/Category.php @@ -55,6 +55,35 @@ class Category extends BaseModel 'base_language' ]; + /** Data used for only parameters + * + * + */ + public const REQUIRED_FIELDS = [ + 'id' + ]; + public const ALLOWED_FIELDS = [ + 'id', + 'parent_id', + 'user_id', + 'tag', + 'slug', + 'type', + 'color', + 'icon', + 'description', + 'role', + 'priority', + 'base_language', + 'created' + ]; + public const ALLOWED_RELATIONSHIPS = [ + 'children' => ['fields' => [], 'relationships' => ['children']], + 'parent' => ['fields' => [], 'relationships' => ['parent']], + 'translations' => ['fields' => [], 'relationships' => ["translations"]], + 'enabled_languages' => ['fields' => ['base_language'], 'relationships' => ['translations']], + ]; + /** * The model's default values for attributes. * diff --git a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php index 79bfedf932..0265ef4acb 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php @@ -7,6 +7,7 @@ use Ushahidi\Modules\V5\DTO\Paging; use Ushahidi\Modules\V5\DTO\CategorySearchFields; use Ushahidi\Core\Tool\SearchData; +use Illuminate\Pagination\LengthAwarePaginator; interface CategoryRepository { @@ -20,7 +21,22 @@ interface CategoryRepository */ public function findById(int $id): Category; - public function fetchAll(Paging $paging); + + /** + * This method will fetch all the Set for the logged user from the database utilising + * Laravel Eloquent ORM and return them as an array + * @param Paging $limit + * @param CategorySearchFields $search_fields + * @param array $fields + * @param array $with + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator + */ + public function paginate( + Paging $paging, + CategorySearchFields $search_fields, + array $fields = [], + array $with = [] + ): LengthAwarePaginator; public function store( ?string $parentId, diff --git a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php index 352d90da8e..b2c97ffeff 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php @@ -3,13 +3,12 @@ namespace Ushahidi\Modules\V5\Repository\Category; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Support\Collection; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\DTO\Paging; use Ushahidi\Modules\V5\DTO\CategorySearchFields; use Ushahidi\Core\Exception\NotFoundException; -use Illuminate\Support\Facades\Auth; use Ushahidi\Core\Tool\SearchData; +use Illuminate\Pagination\LengthAwarePaginator; class EloquentCategoryRepository implements CategoryRepository { @@ -29,59 +28,80 @@ public function setSearchParams(SearchData $searchData) $this->searchData = $searchData; } - private function setSearchCondition(Builder $builder, ?SearchData $search_fields) + + + /** + * This method will fetch a single Category from the database utilising + * Laravel Eloquent ORM. Will throw an exception if provided identifier does + * not exist in the database. + * @param int $id + * @return Category + * @throws NotFoundException + */ + public function findById(int $id): Category { - if ($search_fields === null) { - return $builder; + $category = Category::find($id); + if (!$category instanceof Category) { + throw new NotFoundException('Category not found'); } + return $category; + } - $parent_id = $search_fields->getFilter('parent_id'); - $is_parent = $search_fields->getFilter('is_parent'); - if (isset($parent_id)) { - $builder->where('parent_id', $parent_id); - } elseif ($is_parent === false) { - $builder->whereNull('parent_id'); + private function addTableNamePrefix($fields) + { + $after_update = []; + foreach ($fields as $field) { + $after_update[] = 'tags.' . $field; } + return $after_update; + } - $builder->where(function (Builder $builder) use ($search_fields) { - $keyword = $search_fields->getFilter('keyword'); - $tag = $search_fields->getFilter('tag'); - $type = $search_fields->getFilter('type'); + + private function setSearchCondition(CategorySearchFields $search_fields, $builder) + { - if (isset($keyword)) { - $builder->where('tag', 'LIKE', "%" . $keyword . "%"); - } + if ($search_fields->q()) { + $builder->where('tag', 'LIKE', "%" . $search_fields->q() . "%"); + } - if (isset($tag)) { - $builder->orWhere('tag', 'LIKE', "%" . $keyword . "%"); - } + if ($search_fields->tag()) { + $builder->where('tag', '=', $search_fields->tag()); + } - if (isset($type)) { - $builder->orWhere('type', '=', $type); - } - }); + if ($search_fields->parentId()) { + $builder->where('parent_id', '=', $search_fields->parentId()); + } elseif ($search_fields->isParent()) { + $builder->whereNull('parent_id'); + } - $is_admin = $search_fields->getFilter('is_admin'); - if ($is_admin === false) { - $builder->where(function (Builder $builder) { - // Default always get categories with null roles or has everyone - $builder->whereNull('role'); + if ($search_fields->level()) { + $builder->where('level', '=', $search_fields->level()); + } - // This query isn't working as expected - $builder->orWhere('role', 'LIKE', "%everyone%"); + if ($search_fields->type()) { + $builder->where('type', '=', $search_fields->type()); + } + + if (!$search_fields->isAdmin()) { + // Default search for everyone and guest user + $builder->where(function (Builder $query) { + $query->whereNull('role'); + + $query->orWhere('role', 'LIKE', "%everyone%"); }); - $user_id = $search_fields->getFilter('user_id'); + // is owner + $user_id = $search_fields->userID(); if (isset($user_id) && !is_null($user_id)) { - // Where the user is the owner of the category $builder->orWhere(function (Builder $query) use ($user_id) { $query->where('role', 'LIKE', "%me%") - ->where('user_id', $user_id); + ->where('user_id', '=', $user_id); }); } - $role = $search_fields->getFilter('role'); + // is not admin and has role + $role = $search_fields->role(); if (isset($role) && !is_null($role)) { $builder->orWhere(function (Builder $query) use ($role) { $query->where('role', 'LIKE', "%" . $role . "%"); @@ -92,33 +112,43 @@ private function setSearchCondition(Builder $builder, ?SearchData $search_fields return $builder; } - /** - * This method will fetch a single Category from the database utilising - * Laravel Eloquent ORM. Will throw an exception if provided identifier does - * not exist in the database. - * @param int $id - * @return Category - * @throws NotFoundException + /** + * This method will fetch all the Set for the logged user from the database utilising + * Laravel Eloquent ORM and return them as an array + * @param Paging $limit + * @param CategorySearchFields $search_fields + * @param array $fields + * @param array $with + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function findById(int $id): Category - { - $category = Category::find($id); - if (!$category instanceof Category) { - throw new NotFoundException('Category not found'); + public function paginate( + Paging $paging, + CategorySearchFields $search_fields, + array $fields = [], + array $with = [] + ): LengthAwarePaginator { + $fields = $this->addTableNamePrefix($fields); + // add the order field if not found + if (!in_array('tags.'.$paging->getOrderBy(), $fields)) { + $fields[] = 'tags.'.$paging->getOrderBy(); } - return $category; - } + $query = Category::take($paging->getLimit()) + ->orderBy('tags.'.$paging->getOrderBy(), $paging->getOrder()); - public function fetchAll(Paging $paging) - { - return $this->setSearchCondition( - Category::take($paging->getLimit()) - ->skip($paging->getSkip()) - ->orderBy($paging->getOrderBy(), $paging->getOrder()), - $this->searchData - )->paginate($paging->getLimit()); + $query = $this->setSearchCondition($search_fields, $query); + + if (count($fields)) { + $query->select($fields); + } + if (count($with)) { + $query->with($with); + } + $query->distinct(); + + return $query->paginate($paging->getLimit()); } + public function store( ?string $parentId, ?int $userId, diff --git a/tests/Integration/v5/tags.v5.feature b/tests/Integration/v5/tags.v5.feature index a025b1ba62..daecf1a6e9 100644 --- a/tests/Integration/v5/tags.v5.feature +++ b/tests/Integration/v5/tags.v5.feature @@ -368,7 +368,7 @@ Feature: Testing the Categories API And that the api_url is "api/v5" When I request "/categories" Then the response is JSON - And the "results" property count is "14" + And the "results" property count is "19" Then the guzzle status code should be 200 Scenario: Listing All Tags available to regular users Given that I want to get all "Categories" @@ -376,7 +376,7 @@ Feature: Testing the Categories API And that the api_url is "api/v5" When I request "/categories" Then the response is JSON - And the "results" property count is "7" + And the "results" property count is "9" Then the guzzle status code should be 200 Scenario: Listing All Tags available to non-users @@ -384,7 +384,7 @@ Feature: Testing the Categories API And that the api_url is "api/v5" When I request "/categories" Then the response is JSON - And the "results" property count is "5" + And the "results" property count is "7" Then the guzzle status code should be 200 # # @resetFixture From a5c5296ca239aefa7128eb4bd0a9ca8e8ef9aea6 Mon Sep 17 00:00:00 2001 From: Mh-Asmi Date: Tue, 26 Nov 2024 02:30:41 +0400 Subject: [PATCH 2/5] fix category resource --- .../FetchCategoryByIdQueryHandler.php | 1 - .../V5/Http/Resources/CategoryResource.php | 55 ++++++++++++------- src/Ushahidi/Modules/V5/Models/Category.php | 4 +- .../Category/CategoryRepository.php | 4 +- .../Category/EloquentCategoryRepository.php | 16 +++++- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php index 439eab2c94..ba1c512b0d 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/FetchCategoryByIdQueryHandler.php @@ -34,7 +34,6 @@ public function __invoke(Action $query): Category $this->isSupported($query); return $this->categoryRepository->findById( $query->getId(), - false, array_unique(array_merge( $query->getFields(), $query->getFieldsForRelationship() diff --git a/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php b/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php index fcf30b4953..e461ce47ef 100644 --- a/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php +++ b/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php @@ -2,6 +2,7 @@ namespace Ushahidi\Modules\V5\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource as Resource; +use Ushahidi\Core\Entity\Tag; class CategoryResource extends Resource { @@ -10,6 +11,24 @@ class CategoryResource extends Resource public static $wrap = 'result'; + private function getResourcePrivileges() + { + $authorizer = service('authorizer.tag'); + // Obtain v3 entity from the v5 post model + // Note that we use attributesToArray instead of toArray because the first + // would have the effect of causing unnecessary requests to the database + // (relations are not needed in this case by the authorizer) + $resource_array = $this->resource->attributesToArray(); + unset($resource_array['completed_stages']); + $entity = new Tag($resource_array); + // if there's no user the guards will kick them off already, but if there + // is one we need to check the authorizer to ensure we don't let + // users without admin perms create forms etc + // this is an unfortunate problem with using an old version of lumen + // that doesn't let me do guest user checks without adding more risk. + return $authorizer->getAllowedPrivs($entity); + } + /** * Transform the resource into an array. * @@ -18,27 +37,21 @@ class CategoryResource extends Resource */ public function toArray($request) { - // Preload key relations - $this->resource->loadMissing(['parent', 'children', 'translations']); - return [ - 'id' => $this->id, - 'parent_id' => $this->parent_id, - 'tag' => $this->tag, - 'slug' => $this->slug, - 'type' => $this->type, - 'color' => $this->color, - 'icon' => $this->icon, - 'description' => $this->description, - 'role' => $this->makeRole($this->role), - 'priority' => $this->priority, - 'children' => $this->makeChildren($this->parent, $this->children), - 'parent' => $this->makeParent($this->parent), - 'translations' => new TranslationCollection($this->translations), - 'enabled_languages' => [ - 'default'=> $this->base_language, - 'available' => $this->translations->groupBy('language')->keys() - ] - ]; + $data = $this->resource->toArray(); + if (isset($data['role'])) { + $data['role'] = $this->makeRole($this->role); + } + if (isset($data['children'])) { + $data['children'] = $this->makeChildren($this->parent, $this->children); + } + if (isset($data['parent'])) { + $data['parent'] = $this->makeRole($this->parent); + } + if (isset($data['translations'])) { + $data['translations'] = (new TranslationCollection($this->translations))->toArray(null); + } + $data['allowed_privileges']= $this->getResourcePrivileges(); + return $data; } protected function makeRole($role) diff --git a/src/Ushahidi/Modules/V5/Models/Category.php b/src/Ushahidi/Modules/V5/Models/Category.php index 7d0f36ff21..fcf99bd505 100644 --- a/src/Ushahidi/Modules/V5/Models/Category.php +++ b/src/Ushahidi/Modules/V5/Models/Category.php @@ -78,8 +78,8 @@ class Category extends BaseModel 'created' ]; public const ALLOWED_RELATIONSHIPS = [ - 'children' => ['fields' => [], 'relationships' => ['children']], - 'parent' => ['fields' => [], 'relationships' => ['parent']], + 'children' => ['fields' => ['parent_id'], 'relationships' => ['children']], + 'parent' => ['fields' => ['parent_id'], 'relationships' => ['parent']], 'translations' => ['fields' => [], 'relationships' => ["translations"]], 'enabled_languages' => ['fields' => ['base_language'], 'relationships' => ['translations']], ]; diff --git a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php index 0265ef4acb..2b0db6dd16 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php @@ -16,10 +16,12 @@ interface CategoryRepository * Laravel Eloquent ORM. Will throw an exception if provided identifier does * not exist in the database. * @param int $id + * @param array $fields + * @param array $with * @return Category * @throws NotFoundException */ - public function findById(int $id): Category; + public function findById(int $id, array $fields = [], array $with = []): Category; /** diff --git a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php index b2c97ffeff..08cb00d2ea 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php @@ -35,12 +35,23 @@ public function setSearchParams(SearchData $searchData) * Laravel Eloquent ORM. Will throw an exception if provided identifier does * not exist in the database. * @param int $id + * @param array $fields + * @param array $with * @return Category * @throws NotFoundException */ - public function findById(int $id): Category + public function findById(int $id, array $fields = [], array $with = []): Category { - $category = Category::find($id); + $query = Category::where('id', '=', $id); + if (count($fields)) { + $query->select($fields); + } + if (count($with)) { + $query->with($with); + } + $category = $query->first(); + dd($category); + if (!$category instanceof Category) { throw new NotFoundException('Category not found'); } @@ -136,7 +147,6 @@ public function paginate( ->orderBy('tags.'.$paging->getOrderBy(), $paging->getOrder()); $query = $this->setSearchCondition($search_fields, $query); - if (count($fields)) { $query->select($fields); } From 4b6f005fb3a9d88c738a264430c3ff7cd564db43 Mon Sep 17 00:00:00 2001 From: Mh-Asmi Date: Tue, 26 Nov 2024 20:09:49 +0400 Subject: [PATCH 3/5] fix category translations --- .../Commands/StoreCategoryCommand.php | 19 +++++++- .../Commands/UpdateCategoryCommand.php | 20 ++++++++- .../Handlers/StoreCategoryCommandHandler.php | 40 ++++++++++------- .../Handlers/UpdateCategoryCommandHandler.php | 16 ++++--- .../Http/Controllers/CategoryController.php | 43 +------------------ .../V5/Http/Requests/CategoryRequest.php | 2 +- .../V5/Http/Resources/CategoryResource.php | 4 ++ src/Ushahidi/Modules/V5/Models/Category.php | 4 +- .../Category/CategoryRepository.php | 3 +- .../Category/EloquentCategoryRepository.php | 6 +-- 10 files changed, 83 insertions(+), 74 deletions(-) diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php b/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php index abab193796..37dafa5dc6 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php @@ -64,6 +64,11 @@ class StoreCategoryCommand implements Command * @var array */ private $availableLanguages; + + /** + * @var array + */ + private $translations; public function __construct( ?int $parentId, @@ -76,7 +81,8 @@ public function __construct( int $priority, ?array $role, ?string $defaultLanguage = 'en', - array $availableanguages = [] + array $availableanguages = [], + array $translations ) { $this->parentId = $parentId; $this->tag = $tag; @@ -89,6 +95,7 @@ public function __construct( $this->role = $role; $this->defaultLanguage = $defaultLanguage; $this->availableLanguages = $availableanguages; + $this->translations = $translations; } public static function createFromRequest(CategoryRequest $request): self @@ -109,7 +116,8 @@ public static function createFromRequest(CategoryRequest $request): self (int) $request->input('priority'), $request->input('role'), self::DEFAULT_LANUGAGE, - [] + [], + $request->input('translations')??[] ); } @@ -194,4 +202,11 @@ public function getAvailableLanguages(): array { return $this->availableLanguages; } + /** + * @return array + */ + public function getTranslations(): array + { + return $this->translations; + } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php b/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php index 18bc7227aa..8198e643aa 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php @@ -68,6 +68,11 @@ class UpdateCategoryCommand implements Command * @var ?array */ private $availableLanguages; + + /** + * @var array + */ + private $translations; public function __construct( int $categoryId, @@ -81,7 +86,8 @@ public function __construct( ?int $priority, ?array $role, ?string $defaultLanguage, - ?array $availableLanguages + ?array $availableLanguages = [], + array $translations ) { $this->categoryId = $categoryId; $this->parentId = $parentId; @@ -95,6 +101,7 @@ public function __construct( $this->role = $role; $this->defaultLanguage = $defaultLanguage; $this->availableLanguages = $availableLanguages; + $this->translations = $translations; } public static function fromRequest(int $id, CategoryRequest $request, Category $current_category): self @@ -111,7 +118,8 @@ public static function fromRequest(int $id, CategoryRequest $request, Category $ $request->has('priority')?$request->input('priority'):$current_category->priority, $request->has('role')?$request->input('role'):$current_category->role, self::DEFAULT_LANUGAGE, - [] + [], + $request->input('translations')??[] ); } @@ -174,4 +182,12 @@ public function getAvailableLanguages(): ?array { return $this->availableLanguages; } + + /** + * @return array + */ + public function getTranslations(): array + { + return $this->translations; + } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/StoreCategoryCommandHandler.php b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/StoreCategoryCommandHandler.php index 4f73518296..176f9fe91c 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/StoreCategoryCommandHandler.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/StoreCategoryCommandHandler.php @@ -3,14 +3,14 @@ namespace Ushahidi\Modules\V5\Actions\Category\Handlers; use App\Bus\Action; -use App\Bus\Command\AbstractCommandHandler; use App\Bus\Command\Command; use Ushahidi\Modules\V5\Actions\Category\Commands\StoreCategoryCommand; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\Repository\Category\CategoryRepository; use Illuminate\Support\Facades\Auth; +use Ushahidi\Modules\V5\Actions\V5CommandHandler; -class StoreCategoryCommandHandler extends AbstractCommandHandler +class StoreCategoryCommandHandler extends V5CommandHandler { private $categoryRepository; @@ -46,19 +46,29 @@ public function __invoke(Action $action): int $user_id = Auth::guard()->user()->id ?? null; - return $this->categoryRepository->store( - $parentId, - $user_id, - ucfirst($action->getTag()), - $slug, - $action->getType(), - $action->getDescription(), - $action->getColor(), - $action->getIcon(), - $action->getPriority(), - $action->getRole(), - $action->getDefaultLanguage(), - $action->getAvailableLanguages() + $category = $this->categoryRepository->store( + $parentId, + $user_id, + ucfirst($action->getTag()), + $slug, + $action->getType(), + $action->getDescription(), + $action->getColor(), + $action->getIcon(), + $action->getPriority(), + $action->getRole(), + $action->getDefaultLanguage(), + $action->getAvailableLanguages() + ); + + $this->saveTranslations( + $category, + $category->toArray(), + $action->getTranslations(), + $category->id, + 'category' ); + + return $category->id; } } diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/UpdateCategoryCommandHandler.php b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/UpdateCategoryCommandHandler.php index a6add7c1b3..225f8a0961 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Handlers/UpdateCategoryCommandHandler.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Handlers/UpdateCategoryCommandHandler.php @@ -3,15 +3,14 @@ namespace Ushahidi\Modules\V5\Actions\Category\Handlers; use App\Bus\Action; -use App\Bus\Command\AbstractCommandHandler; use App\Bus\Command\Command; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\Repository\Category\CategoryRepository; use Ushahidi\Modules\V5\Actions\Category\Commands\UpdateCategoryCommand; use Illuminate\Support\Facades\Auth; +use Ushahidi\Modules\V5\Actions\V5CommandHandler; -class UpdateCategoryCommandHandler extends AbstractCommandHandler +class UpdateCategoryCommandHandler extends V5CommandHandler { private $categoryRepository; @@ -49,8 +48,15 @@ public function __invoke(Action $action): Category $action->getDefaultLanguage(), $action->getAvailableLanguages() ); - - return $this->categoryRepository + $category = $this->categoryRepository ->findById($action->getCategoryId()); + $this->updateTranslations( + $category, + $category->toArray(), + $action->getTranslations(), + $category->id, + 'category' + ); + return $category; } } diff --git a/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php b/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php index 14f1a53e19..17e72d097d 100644 --- a/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php +++ b/src/Ushahidi/Modules/V5/Http/Controllers/CategoryController.php @@ -88,25 +88,6 @@ public function store(CategoryRequest $request) $category = $this->getCategory($id, Category::ALLOWED_FIELDS); - DB::beginTransaction(); - try { - $errors = $this->saveTranslations( - $category, - $category->toArray(), - $request->input('translations', []), - $category->id, - 'category' - ); - if (!empty($errors)) { - DB::rollback(); - return self::make422($errors, 'translation'); - } - DB::commit(); - } catch (\Exception $e) { - DB::rollback(); - return self::make500($e->getMessage()); - } - return new CategoryResource($category); } @@ -123,28 +104,8 @@ public function update(int $id, CategoryRequest $request) $category = $this->getCategory($id, Category::ALLOWED_FIELDS, []); $this->authorize('update', $category); - DB::beginTransaction(); - try { - $command = UpdateCategoryCommand::fromRequest($id, $request, $category); - $category = $this->commandBus->handle($command); - $errors = $this->updateTranslations( - new Category(), - $category->toArray(), - $request->input('translations') ?? [], - $category->id, - 'category' - ); - if (!empty($errors)) { - DB::rollback(); - // To do : change the return results to Exception - return self::make422($errors, 'translation'); - } - DB::commit(); - return new CategoryResource($category); - } catch (\Exception $e) { - DB::rollback(); - throw $e; - } + $this->commandBus->handle(UpdateCategoryCommand::fromRequest($id, $request, $category)); + return new CategoryResource($this->getCategory($id, Category::ALLOWED_FIELDS)); } /** diff --git a/src/Ushahidi/Modules/V5/Http/Requests/CategoryRequest.php b/src/Ushahidi/Modules/V5/Http/Requests/CategoryRequest.php index 45ed977ff9..7fdd68587e 100644 --- a/src/Ushahidi/Modules/V5/Http/Requests/CategoryRequest.php +++ b/src/Ushahidi/Modules/V5/Http/Requests/CategoryRequest.php @@ -56,7 +56,7 @@ public function rules($data = []) 'role' => [ function ($attribute, $value, $fail) use ($parent) { // ... and check if the role matches its parent - if ($parent && $parent->getAttribute('role') != $value) { + if ($parent && $parent['role'] != $value) { return $fail(trans('validation.child_parent_role_match')); } if (is_array($value) && empty($value)) { diff --git a/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php b/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php index e461ce47ef..ba100a5930 100644 --- a/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php +++ b/src/Ushahidi/Modules/V5/Http/Resources/CategoryResource.php @@ -49,6 +49,10 @@ public function toArray($request) } if (isset($data['translations'])) { $data['translations'] = (new TranslationCollection($this->translations))->toArray(null); + $data['enabled_languages'] = [ + 'default'=> $this->base_language, + 'available' => $this->translations->groupBy('language')->keys() + ]; } $data['allowed_privileges']= $this->getResourcePrivileges(); return $data; diff --git a/src/Ushahidi/Modules/V5/Models/Category.php b/src/Ushahidi/Modules/V5/Models/Category.php index fcf99bd505..5da581d0ec 100644 --- a/src/Ushahidi/Modules/V5/Models/Category.php +++ b/src/Ushahidi/Modules/V5/Models/Category.php @@ -24,7 +24,7 @@ class Category extends BaseModel * @var array */ protected $hidden = [ - 'description', + // 'description', ]; /** @@ -114,7 +114,7 @@ public function parent() public function children() { - return $this->hasMany(Category::class, 'parent_id', 'id')->withoutGlobalScopes(); + return $this->hasMany(Category::class, 'parent_id', 'id')->withoutGlobalScopes()->with('translations'); } /** diff --git a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php index 2b0db6dd16..1e20a640ac 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/CategoryRepository.php @@ -2,7 +2,6 @@ namespace Ushahidi\Modules\V5\Repository\Category; -use Illuminate\Support\Collection; use Ushahidi\Modules\V5\Models\Category; use Ushahidi\Modules\V5\DTO\Paging; use Ushahidi\Modules\V5\DTO\CategorySearchFields; @@ -53,7 +52,7 @@ public function store( ?array $role, string $defaultBaseLanguage, array $availableLanguages - ): int; + ): Category; public function slugExists(string $slug): bool; public function update( int $id, diff --git a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php index 08cb00d2ea..0c4c57cf83 100644 --- a/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php +++ b/src/Ushahidi/Modules/V5/Repository/Category/EloquentCategoryRepository.php @@ -50,8 +50,6 @@ public function findById(int $id, array $fields = [], array $with = []): Categor $query->with($with); } $category = $query->first(); - dd($category); - if (!$category instanceof Category) { throw new NotFoundException('Category not found'); } @@ -172,7 +170,7 @@ public function store( ?array $role, string $defaultBaseLanguage, array $availableLanguages - ): int { + ): Category { $input = array_filter([ 'parent_id' => $parentId, 'user_id' => $userId, @@ -192,7 +190,7 @@ public function store( $isSaved = $category->saveOrFail(); - return $category->id; + return $category; } public function slugExists(string $slug): bool From 667c2b19a4a4600a05da38df222ac21b3ae07b18 Mon Sep 17 00:00:00 2001 From: Mh-Asmi Date: Tue, 26 Nov 2024 20:21:39 +0400 Subject: [PATCH 4/5] fixlint --- .../V5/Actions/Category/Commands/StoreCategoryCommand.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php b/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php index 37dafa5dc6..bd89639bb3 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Commands/StoreCategoryCommand.php @@ -80,9 +80,9 @@ public function __construct( ?string $icon, int $priority, ?array $role, + array $translations, ?string $defaultLanguage = 'en', - array $availableanguages = [], - array $translations + array $availableanguages = [] ) { $this->parentId = $parentId; $this->tag = $tag; @@ -115,9 +115,9 @@ public static function createFromRequest(CategoryRequest $request): self $request->input('icon'), (int) $request->input('priority'), $request->input('role'), + $request->input('translations')??[], self::DEFAULT_LANUGAGE, - [], - $request->input('translations')??[] + [] ); } From 316727f1c7f2df65c5ed9332be87babff8e59ace Mon Sep 17 00:00:00 2001 From: Mh-Asmi Date: Tue, 26 Nov 2024 20:30:07 +0400 Subject: [PATCH 5/5] fix lint for catevory update --- .../Actions/Category/Commands/UpdateCategoryCommand.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php b/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php index 8198e643aa..f52c80ec23 100644 --- a/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php +++ b/src/Ushahidi/Modules/V5/Actions/Category/Commands/UpdateCategoryCommand.php @@ -85,9 +85,9 @@ public function __construct( ?string $icon, ?int $priority, ?array $role, + array $translations, ?string $defaultLanguage, - ?array $availableLanguages = [], - array $translations + ?array $availableLanguages = [] ) { $this->categoryId = $categoryId; $this->parentId = $parentId; @@ -117,9 +117,9 @@ public static function fromRequest(int $id, CategoryRequest $request, Category $ $request->has('icon')?$request->input('icon'):$current_category->icon, $request->has('priority')?$request->input('priority'):$current_category->priority, $request->has('role')?$request->input('role'):$current_category->role, + $request->input('translations')??[], self::DEFAULT_LANUGAGE, - [], - $request->input('translations')??[] + [] ); }