refactor copy() method if ProductController.php to be a lot smaller

and split the logic into smaller functions for better readability
This commit is contained in:
Herbert Maschke 2020-08-03 12:40:52 +02:00
parent 0509f45762
commit 5db4797158
2 changed files with 235 additions and 160 deletions

View File

@ -4,8 +4,6 @@ namespace Webkul\Product\Http\Controllers;
use Exception;
use Illuminate\Support\Facades\Event;
use Webkul\Attribute\Models\Attribute;
use Webkul\Product\Models\ProductFlat;
use Webkul\Product\Helpers\ProductType;
use Illuminate\Support\Facades\Storage;
use Webkul\Core\Contracts\Validations\Slug;
@ -249,77 +247,7 @@ class ProductController extends Controller
*/
public function copy(int $productId)
{
$originalProduct = $this->productRepository
->findOrFail($productId)
->load('attribute_family')
->load('categories')
->load('customer_group_prices')
->load('inventories')
->load('inventory_sources');
$copiedProduct = $originalProduct
->replicate()
->fill([
// the sku and url_key needs to be unique and should be entered again newly by the admin:
'sku' => 'temporary-sku-' . substr(md5(microtime()), 0, 6),
]);
$copiedProduct->save();
$attributeName = Attribute::query()->where(['code' => 'name'])->firstOrFail();
$attributeSku = Attribute::query()->where(['code' => 'sku'])->firstOrFail();
$attributeStatus = Attribute::query()->where(['code' => 'status'])->firstOrFail();
$attributeUrlKey = Attribute::query()->where(['code' => 'url_key'])->firstOrFail();
$newProductFlat = new ProductFlat();
// only obey copied locale and channel:
if (isset($originalProduct->product_flats[0])) {
$newProductFlat = $originalProduct->product_flats[0]->replicate();
}
$newProductFlat->product_id = $copiedProduct->id;
foreach ($originalProduct->attribute_values as $oldValue) {
$newValue = $oldValue->replicate();
if ($oldValue->attribute_id === $attributeName->id) {
$newValue->text_value = __('admin::app.copy-of') . ' ' . $originalProduct->name;
$newProductFlat->name = __('admin::app.copy-of') . ' ' . $originalProduct->name;
}
if ($oldValue->attribute_id === $attributeUrlKey->id) {
$newValue->text_value = __('admin::app.copy-of-slug') . '-' . $originalProduct->url_key;
$newProductFlat->url_key = __('admin::app.copy-of-slug') . '-' . $originalProduct->url_key;
}
if ($oldValue->attribute_id === $attributeSku->id) {
$newValue->text_value = $copiedProduct->sku;
$newProductFlat->sku = $copiedProduct->sku;
}
if ($oldValue->attribute_id === $attributeStatus->id) {
$newValue->boolean_value = 0;
$newProductFlat->status = 0;
}
$copiedProduct->attribute_values()->save($newValue);
}
$newProductFlat->save();
foreach ($originalProduct->categories as $category) {
$copiedProduct->categories()->save($category);
}
foreach ($originalProduct->inventories as $inventory) {
$copiedProduct->inventories()->save($inventory);
}
foreach ($originalProduct->customer_group_prices as $customer_group_price) {
$copiedProduct->customer_group_prices()->save($customer_group_price);
}
$copiedProduct = $this->productRepository->copy($productId);
return redirect()->to(route('admin.catalog.products.edit', ['id' => $copiedProduct->id]));
}

View File

@ -2,14 +2,18 @@
namespace Webkul\Product\Repositories;
use Illuminate\Pagination\Paginator;
use Webkul\Product\Models\Product;
use Illuminate\Support\Facades\DB;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Webkul\Attribute\Repositories\AttributeRepository;
use Illuminate\Pagination\Paginator;
use Webkul\Core\Eloquent\Repository;
use Illuminate\Support\Facades\Event;
use Webkul\Attribute\Models\Attribute;
use Webkul\Product\Models\ProductFlat;
use Illuminate\Container\Container as App;
use Illuminate\Pagination\LengthAwarePaginator;
use Webkul\Product\Models\ProductAttributeValueProxy;
use Webkul\Attribute\Repositories\AttributeRepository;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class ProductRepository extends Repository
{
@ -23,8 +27,9 @@ class ProductRepository extends Repository
/**
* Create a new repository instance.
*
* @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param \Illuminate\Container\Container $app
* @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param \Illuminate\Container\Container $app
*
* @return void
*/
public function __construct(
@ -48,7 +53,8 @@ class ProductRepository extends Repository
}
/**
* @param array $data
* @param array $data
*
* @return \Webkul\Product\Contracts\Product
*/
public function create(array $data)
@ -65,9 +71,10 @@ class ProductRepository extends Repository
}
/**
* @param array $data
* @param int $id
* @param string $attribute
* @param array $data
* @param int $id
* @param string $attribute
*
* @return \Webkul\Product\Contracts\Product
*/
public function update(array $data, $id, $attribute = "id")
@ -88,7 +95,8 @@ class ProductRepository extends Repository
}
/**
* @param int $id
* @param int $id
*
* @return void
*/
public function delete($id)
@ -101,7 +109,8 @@ class ProductRepository extends Repository
}
/**
* @param int $categoryId
* @param int $categoryId
*
* @return \Illuminate\Support\Collection
*/
public function getAll($categoryId = null)
@ -111,21 +120,21 @@ class ProductRepository extends Repository
if (core()->getConfigData('catalog.products.storefront.products_per_page')) {
$pages = explode(',', core()->getConfigData('catalog.products.storefront.products_per_page'));
$perPage = isset($params['limit']) ? (!empty($params['limit']) ? $params['limit'] : 9) : current($pages);
$perPage = isset($params['limit']) ? (! empty($params['limit']) ? $params['limit'] : 9) : current($pages);
} else {
$perPage = isset($params['limit']) && !empty($params['limit']) ? $params['limit'] : 9;
$perPage = isset($params['limit']) && ! empty($params['limit']) ? $params['limit'] : 9;
}
$page = Paginator::resolveCurrentPage('page');
$repository = app(ProductFlatRepository::class)->scopeQuery(function($query) use($params, $categoryId) {
$repository = app(ProductFlatRepository::class)->scopeQuery(function ($query) use ($params, $categoryId) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
$qb = $query->distinct()
->select('product_flat.*')
->join('product_flat as variants', 'product_flat.id', '=', DB::raw('COALESCE('.DB::getTablePrefix().'variants.parent_id, '.DB::getTablePrefix().'variants.id)'))
->join('product_flat as variants', 'product_flat.id', '=', DB::raw('COALESCE(' . DB::getTablePrefix() . 'variants.parent_id, ' . DB::getTablePrefix() . 'variants.id)'))
->leftJoin('product_categories', 'product_categories.product_id', '=', 'product_flat.product_id')
->leftJoin('product_attribute_values', 'product_attribute_values.product_id', '=', 'variants.product_id')
->where('product_flat.channel', $channel)
@ -149,25 +158,25 @@ class ProductRepository extends Repository
# sort direction
$orderDirection = 'asc';
if( isset($params['order']) && in_array($params['order'], ['desc', 'asc']) ){
if (isset($params['order']) && in_array($params['order'], ['desc', 'asc'])) {
$orderDirection = $params['order'];
} else {
$sortOptions = $this->getDefaultSortByOption();
$orderDirection = !empty($sortOptions) ? $sortOptions[1] : 'asc';
$orderDirection = ! empty($sortOptions) ? $sortOptions[1] : 'asc';
}
if (isset($params['sort'])) {
$this->checkSortAttributeAndGenerateQuery($qb, $params['sort'], $orderDirection);
} else {
$sortOptions = $this->getDefaultSortByOption();
if (!empty($sortOptions)) {
if (! empty($sortOptions)) {
$this->checkSortAttributeAndGenerateQuery($qb, $sortOptions[0], $orderDirection);
}
}
if ( $priceFilter = request('price') ){
if ($priceFilter = request('price')) {
$priceRange = explode(',', $priceFilter);
if( count($priceRange) > 0 ) {
if (count($priceRange) > 0) {
$qb->where('variants.min_price', '>=', core()->convertToBasePrice($priceRange[0]));
$qb->where('variants.min_price', '<=', core()->convertToBasePrice(end($priceRange)));
}
@ -178,8 +187,8 @@ class ProductRepository extends Repository
request()->except(['price'])
));
if ( count($attributeFilters) > 0 ) {
$qb->where(function ($filterQuery) use($attributeFilters){
if (count($attributeFilters) > 0) {
$qb->where(function ($filterQuery) use ($attributeFilters) {
foreach ($attributeFilters as $attribute) {
$filterQuery->orWhere(function ($attributeQuery) use ($attribute) {
@ -196,7 +205,7 @@ class ProductRepository extends Repository
$attributeQuery->where(function ($attributeValueQuery) use ($column, $filterInputValues) {
foreach ($filterInputValues as $filterValue) {
if (!is_numeric($filterValue)) {
if (! is_numeric($filterValue)) {
continue;
}
$attributeValueQuery->orWhereRaw("find_in_set(?, {$column})", [$filterValue]);
@ -239,9 +248,9 @@ class ProductRepository extends Repository
$items = [];
}
$results = new \Illuminate\Pagination\LengthAwarePaginator($items, $count, $perPage, $page, [
$results = new LengthAwarePaginator($items, $count, $perPage, $page, [
'path' => request()->url(),
'query' => request()->query()
'query' => request()->query(),
]);
return $results;
@ -250,8 +259,9 @@ class ProductRepository extends Repository
/**
* Retrive product from slug
*
* @param string $slug
* @param string $columns
* @param string $slug
* @param string $columns
*
* @return \Webkul\Product\Contracts\Product
*/
public function findBySlugOrFail($slug, $columns = null)
@ -274,7 +284,8 @@ class ProductRepository extends Repository
/**
* Retrieve product from slug without throwing an exception (might return null)
*
* @param string $slug
* @param string $slug
*
* @return \Webkul\Product\Contracts\ProductFlat
*/
public function findBySlug($slug)
@ -293,19 +304,19 @@ class ProductRepository extends Repository
*/
public function getNewProducts()
{
$results = app(ProductFlatRepository::class)->scopeQuery(function($query) {
$results = app(ProductFlatRepository::class)->scopeQuery(function ($query) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.new', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->inRandomOrder();
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.new', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->inRandomOrder();
})->paginate(4);
return $results;
@ -318,19 +329,19 @@ class ProductRepository extends Repository
*/
public function getFeaturedProducts()
{
$results = app(ProductFlatRepository::class)->scopeQuery(function($query) {
$results = app(ProductFlatRepository::class)->scopeQuery(function ($query) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.featured', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->inRandomOrder();
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.featured', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->inRandomOrder();
})->paginate(4);
return $results;
@ -339,7 +350,8 @@ class ProductRepository extends Repository
/**
* Search Product by Attribute
*
* @param string $term
* @param string $term
*
* @return \Illuminate\Support\Collection
*/
public function searchProductByAttribute($term)
@ -349,27 +361,27 @@ class ProductRepository extends Repository
$locale = request()->get('locale') ?: app()->getLocale();
if (config('scout.driver') == 'algolia') {
$results = app(ProductFlatRepository::class)->getModel()::search('query', function ($searchDriver, string $query, array $options) use($term, $channel, $locale) {
$results = app(ProductFlatRepository::class)->getModel()::search('query', function ($searchDriver, string $query, array $options) use ($term, $channel, $locale) {
$queries = explode('_', $term);
$options['similarQuery'] = array_map('trim', $queries);
$searchDriver->setSettings([
'attributesForFaceting' => [
"searchable(locale)",
"searchable(channel)"
]
"searchable(locale)",
"searchable(channel)",
],
]);
$options['facetFilters'] = ['locale:' . $locale, 'channel:' . $channel];
$options['facetFilters'] = ['locale:' . $locale, 'channel:' . $channel];
return $searchDriver->search($query, $options);
})
->where('status', 1)
->where('visible_individually', 1)
->orderBy('product_id', 'desc')
->paginate(16);
} else if(config('scout.driver') == 'elastic') {
->where('status', 1)
->where('visible_individually', 1)
->orderBy('product_id', 'desc')
->paginate(16);
} else if (config('scout.driver') == 'elastic') {
$queries = explode('_', $term);
$results = app(ProductFlatRepository::class)->getModel()::search(implode(' OR ', $queries))
@ -380,23 +392,23 @@ class ProductRepository extends Repository
->orderBy('product_id', 'desc')
->paginate(16);
} else {
$results = app(ProductFlatRepository::class)->scopeQuery(function($query) use($term, $channel, $locale) {
$results = app(ProductFlatRepository::class)->scopeQuery(function ($query) use ($term, $channel, $locale) {
return $query->distinct()
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->whereNotNull('product_flat.url_key')
->where(function($subQuery) use ($term) {
$queries = explode('_', $term);
->addSelect('product_flat.*')
->where('product_flat.status', 1)
->where('product_flat.visible_individually', 1)
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->whereNotNull('product_flat.url_key')
->where(function ($subQuery) use ($term) {
$queries = explode('_', $term);
foreach (array_map('trim', $queries) as $value) {
$subQuery->orWhere('product_flat.name', 'like', '%' . urldecode($value) . '%')
->orWhere('product_flat.short_description', 'like', '%' . urldecode($value) . '%');
}
})
->orderBy('product_id', 'desc');
foreach (array_map('trim', $queries) as $value) {
$subQuery->orWhere('product_flat.name', 'like', '%' . urldecode($value) . '%')
->orWhere('product_flat.short_description', 'like', '%' . urldecode($value) . '%');
}
})
->orderBy('product_id', 'desc');
})->paginate(16);
}
@ -406,7 +418,8 @@ class ProductRepository extends Repository
/**
* Returns product's super attribute with options
*
* @param \Webkul\Product\Contracts\Product $product
* @param \Webkul\Product\Contracts\Product $product
*
* @return \Illuminate\Support\Collection
*/
public function getSuperAttributes($product)
@ -432,35 +445,54 @@ class ProductRepository extends Repository
/**
* Search simple products for grouped product association
*
* @param string $term
* @param string $term
*
* @return \Illuminate\Support\Collection
*/
public function searchSimpleProducts($term)
{
return app(ProductFlatRepository::class)->scopeQuery(function($query) use($term) {
return app(ProductFlatRepository::class)->scopeQuery(function ($query) use ($term) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->addSelect('product_flat.product_id as id')
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->where('products.type', 'simple')
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->where('product_flat.name', 'like', '%' . urldecode($term) . '%')
->orderBy('product_id', 'desc');
->addSelect('product_flat.*')
->addSelect('product_flat.product_id as id')
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->where('products.type', 'simple')
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->where('product_flat.name', 'like', '%' . urldecode($term) . '%')
->orderBy('product_id', 'desc');
})->get();
}
/**
* Copy a product. Is usually called by the copy() function of the ProductController.
*
* @param int $sourceProductId the id of the product that should be copied
*/
public function copy(int $sourceProductId): Product
{
$originalProduct = $this->loadOriginalProduct($sourceProductId);
$copiedProduct = $this->persistCopiedProduct($originalProduct);
$this->persistAttributeValues($originalProduct, $copiedProduct);
$this->persistRelations($originalProduct, $copiedProduct);
return $copiedProduct;
}
/**
* Get default sort by option
*
* @return array
*/
private function getDefaultSortByOption()
{
{
$value = core()->getConfigData('catalog.products.storefront.sort_by');
$config = $value ? $value : 'name-desc';
@ -471,9 +503,10 @@ class ProductRepository extends Repository
/**
* Check sort attribute and generate query
*
* @param object $query
* @param string $sort
* @param string $direction
* @param object $query
* @param string $sort
* @param string $direction
*
* @return object
*/
private function checkSortAttributeAndGenerateQuery($query, $sort, $direction)
@ -490,4 +523,118 @@ class ProductRepository extends Repository
return $query;
}
/**
* @param int $sourceProductId
*
* @return mixed
*/
private function loadOriginalProduct(int $sourceProductId)
{
$originalProduct = $this
->findOrFail($sourceProductId)
->load('attribute_family')
->load('categories')
->load('customer_group_prices')
->load('inventories')
->load('inventory_sources');
return $originalProduct;
}
/**
* @param $originalProduct
*
* @return mixed
*/
private function persistCopiedProduct($originalProduct)
{
$copiedProduct = $originalProduct
->replicate()
->fill([
// the sku and url_key needs to be unique and should be entered again newly by the admin:
'sku' => 'temporary-sku-' . substr(md5(microtime()), 0, 6),
]);
$copiedProduct->save();
return $copiedProduct;
}
/**
* Gather the ids of the necessary product attributes.
* Throw an Exception if one of these 'basic' attributes are missing for some reason.
*/
private function gatherAttributeIds(): array
{
$ids = [];
foreach (['name', 'sku', 'status', 'url_key'] as $code) {
$ids[$code] = Attribute::query()->where(['code' => $code])->firstOrFail()->id;
}
return $ids;
}
private function persistAttributeValues(Product $originalProduct, Product $copiedProduct): void
{
$attributeIds = $this->gatherAttributeIds();
$newProductFlat = new ProductFlat();
// only obey copied locale and channel:
if (isset($originalProduct->product_flats[0])) {
$newProductFlat = $originalProduct->product_flats[0]->replicate();
}
$newProductFlat->product_id = $copiedProduct->id;
foreach ($originalProduct->attribute_values as $oldValue) {
$newValue = $oldValue->replicate();
if ($oldValue->attribute_id === $attributeIds['name']) {
$newValue->text_value = __('admin::app.copy-of') . ' ' . $originalProduct->name;
$newProductFlat->name = __('admin::app.copy-of') . ' ' . $originalProduct->name;
}
if ($oldValue->attribute_id === $attributeIds['url_key']) {
$newValue->text_value = __('admin::app.copy-of-slug') . '-' . $originalProduct->url_key;
$newProductFlat->url_key = __('admin::app.copy-of-slug') . '-' . $originalProduct->url_key;
}
if ($oldValue->attribute_id === $attributeIds['sku']) {
$newValue->text_value = $copiedProduct->sku;
$newProductFlat->sku = $copiedProduct->sku;
}
if ($oldValue->attribute_id === $attributeIds['status']) {
$newValue->boolean_value = 0;
$newProductFlat->status = 0;
}
$copiedProduct->attribute_values()->save($newValue);
}
$newProductFlat->save();
}
/**
* @param $originalProduct
* @param $copiedProduct
*/
private function persistRelations($originalProduct, $copiedProduct): void
{
foreach ($originalProduct->categories as $category) {
$copiedProduct->categories()->save($category);
}
foreach ($originalProduct->inventories as $inventory) {
$copiedProduct->inventories()->save($inventory);
}
foreach ($originalProduct->customer_group_prices as $customer_group_price) {
$copiedProduct->customer_group_prices()->save($customer_group_price);
}
}
}