Skip to content

Commit

Permalink
Связи товара и мультикатегории
Browse files Browse the repository at this point in the history
  • Loading branch information
bezumkin committed Aug 23, 2023
1 parent 8b3d543 commit b87466e
Show file tree
Hide file tree
Showing 20 changed files with 502 additions and 86 deletions.
5 changes: 5 additions & 0 deletions core/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ static function (RouteCollectorProxy $group) {
$group->any('/categories[/{id}]', App\Controllers\Admin\Categories::class);
$group->any('/products[/{id}]', App\Controllers\Admin\Products::class);
$group->any('/product/{product_id}/files[/{file_id}]', App\Controllers\Admin\Product\Files::class);
$group->any(
'/product/{product_id}/categories[/{category_id}]',
App\Controllers\Admin\Product\Categories::class
);
$group->any('/product/{product_id}/links[/{link_id}]', App\Controllers\Admin\Product\Links::class);
$group->any('/orders[/{id}]', App\Controllers\Admin\Orders::class);
}
);
Expand Down
6 changes: 6 additions & 0 deletions core/src/Controllers/Admin/Categories.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ protected function beforeCount(Builder $c): Builder
$c->orWhere('description', 'LIKE', "%$query%");
});
}
if ($exclude = $this->getProperty('exclude')) {
if (!is_array($exclude)) {
$exclude = explode(',', $exclude);
}
$c->whereNotIn('id', array_map('intval', $exclude));
}

return $c;
}
Expand Down
44 changes: 44 additions & 0 deletions core/src/Controllers/Admin/Product/Categories.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace App\Controllers\Admin\Product;

use App\Controllers\Traits\ProductPropertyController;
use App\Models\ProductCategory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Vesp\Controllers\ModelController;

class Categories extends ModelController
{
use ProductPropertyController;

protected $scope = 'products';
protected $model = ProductCategory::class;
protected $primaryKey = ['product_id', 'category_id'];

protected function beforeCount(Builder $c): Builder
{
$c->where('product_id', $this->product->id);

if ($query = trim($this->getProperty('query', ''))) {
$c->whereHas('category', static function (Builder $c) use ($query) {
$c->whereHas('translations', static function (Builder $c) use ($query) {
$c->where('title', 'LIKE', "%$query%");
$c->orWhere('description', 'LIKE', "%$query%");
});
});
}

return $c;
}

protected function afterCount(Builder $c): Builder
{
$c->with('category', static function(BelongsTo $c) {
$c->with('translations:category_id,lang,title');
$c->with('file:id,updated_at');
});

return $c;
}
}
19 changes: 3 additions & 16 deletions core/src/Controllers/Admin/Product/Files.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace App\Controllers\Admin\Product;

use App\Controllers\Traits\ProductPropertyController;
use App\Models\File;
use App\Models\Product;
use App\Models\ProductFile;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -12,24 +12,11 @@

class Files extends ModelController
{
use ProductPropertyController;

protected $scope = 'products';
protected $model = ProductFile::class;
protected $primaryKey = ['product_id', 'file_id'];
/** @var Product product */
protected $product;

public function checkScope(string $method): ?ResponseInterface
{
if ($check = parent::checkScope($method)) {
return $check;
}

if (!$this->product = Product::query()->find($this->getProperty('product_id'))) {
return $this->failure('', 404);
}

return null;
}

protected function beforeGet(Builder $c): Builder
{
Expand Down
49 changes: 49 additions & 0 deletions core/src/Controllers/Admin/Product/Links.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Controllers\Admin\Product;

use App\Controllers\Traits\ProductPropertyController;
use App\Models\ProductLink;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Vesp\Controllers\ModelController;

class Links extends ModelController
{
use ProductPropertyController;

protected $scope = 'products';
protected $model = ProductLink::class;
protected $primaryKey = ['product_id', 'link_id'];

protected function beforeCount(Builder $c): Builder
{
$c->where('product_id', $this->product->id);

if ($query = trim($this->getProperty('query', ''))) {
$c->whereHas('link', static function (Builder $c) use ($query) {
$c->whereHas('translations', static function (Builder $c) use ($query) {
$c->where('title', 'LIKE', "%$query%");
$c->orWhere('subtitle', 'LIKE', "%$query%");
$c->orWhere('description', 'LIKE', "%$query%");
$c->orWhere('content', 'LIKE', "%$query%");
});
});
}

return $c;
}

protected function afterCount(Builder $c): Builder
{
$c->with('link', function(BelongsTo $c) {
$c->with('category:id,uri,active', 'category.translations:category_id,lang,title');
$c->with('translations:product_id,lang,title');
if (!$this->getProperty('combo')) {
$c->with('firstFile');
}
});

return $c;
}
}
24 changes: 24 additions & 0 deletions core/src/Controllers/Traits/ProductPropertyController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Controllers\Traits;

use App\Models\Product;
use Psr\Http\Message\ResponseInterface;

trait ProductPropertyController
{
protected ?Product $product;

public function checkScope(string $method): ?ResponseInterface
{
if ($check = parent::checkScope($method)) {
return $check;
}

if (!$this->product = Product::query()->find($this->getProperty('product_id'))) {
return $this->failure('', 404);
}

return null;
}
}
5 changes: 5 additions & 0 deletions core/src/Models/ProductCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class ProductCategory extends Model
protected $primaryKey = ['product_id', 'category_id'];
protected $guarded = [];

protected function getCurrentRank(): int
{
return $this->newQuery()->where('category_id', $this->category_id)->max('rank') + 1;
}

public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
Expand Down
5 changes: 5 additions & 0 deletions core/src/Models/ProductLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class ProductLink extends Model
protected $primaryKey = ['product_id', 'link_id'];
protected $guarded = [];

protected function getCurrentRank(): int
{
return $this->newQuery()->where('product_id', $this->product_id)->max('rank') + 1;
}

public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/admin/assets/scss/_variables.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
// Your variables here

$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
2xl: 1400px,
3xl: 1600px,
) !default;

$container-max-widths: (
sm: 540px,
md: 740px,
lg: 1060px,
xl: 1240px,
2xl: 1340px,
3xl: 1540px
) !default;

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
Expand Down
18 changes: 3 additions & 15 deletions frontend/src/admin/components/forms/category.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,7 @@
</b-row>

<b-form-group :label="$t('models.category.parent')">
<vesp-input-combo-box
v-model="record.parent_id"
url="admin/categories"
sort="rank"
:format-value="(item) => $translate(item.translations)"
:filter-props="{exclude: record.id}"
>
<template #default="{item}">
<div :class="{'text-muted': !item.active}">
{{ $translate(item.translations) }}
<div class="small text-muted">{{ item.uri }}</div>
</div>
</template>
</vesp-input-combo-box>
<input-category v-model="record.parent_id" />
</b-form-group>

<b-form-group v-lang-icon="lang" :label="$t('models.category.description')">
Expand All @@ -75,10 +62,11 @@ import InputAlias from '@/components/inputs/alias.vue'
import FileUpload from '@/components/file-upload.vue'
import Translations from '@/mixins/translations.js'
import LangTabs from '@/components/lang-tabs.vue'
import InputCategory from '@/components/inputs/category.vue'
export default {
name: 'FormCategory',
components: {LangTabs, FileUpload, InputAlias},
components: {InputCategory, LangTabs, FileUpload, InputAlias},
mixins: [Translations],
props: {
value: {
Expand Down
18 changes: 3 additions & 15 deletions frontend/src/admin/components/forms/product.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,7 @@
</b-form-group>

<b-form-group :label="$t('models.product.category')">
<vesp-input-combo-box
v-model="record.category_id"
url="admin/categories"
sort="rank"
:format-value="(item) => $translate(item.translations)"
required
>
<template #default="{item}">
<div :class="{'text-muted': !item.active}">
{{ $translate(item.translations) }}
<div class="small text-muted">{{ item.uri }}</div>
</div>
</template>
</vesp-input-combo-box>
<input-category v-model="record.category_id" />
</b-form-group>

<b-row>
Expand Down Expand Up @@ -154,10 +141,11 @@
import InputAlias from '@/components/inputs/alias.vue'
import Translations from '@/mixins/translations.js'
import LangTabs from '@/components/lang-tabs.vue'
import InputCategory from '@/components/inputs/category.vue'
export default {
name: 'FormProduct',
components: {LangTabs, InputAlias},
components: {InputCategory, LangTabs, InputAlias},
mixins: [Translations],
props: {
value: {
Expand Down
66 changes: 66 additions & 0 deletions frontend/src/admin/components/inputs/category.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<template>
<vesp-input-combo-box v-model="myValue" url="admin/categories" v-bind="$props" v-on="$listeners">
<template #default="{item}">
<div :class="{'text-muted': !item.active}">
{{ $translate(item.translations) }}
<div class="small text-muted">{{ item.uri }}</div>
</div>
</template>
</vesp-input-combo-box>
</template>

<script>
import {BFormInput} from 'bootstrap-vue'
export default {
name: 'InputCategory',
props: {
...BFormInput.extendOptions.props,
value: {
type: [String, Number],
default: null,
required: false,
},
placeholder: {
type: String,
default() {
return this.$t('models.category.title_one')
},
},
sort: {
type: String,
default: 'rank',
},
dir: {
type: String,
default: 'asc',
},
emptyText: {
type: String,
default: 'No results',
},
filterProps: {
type: Object,
default() {
return {}
},
},
formatValue: {
type: Function,
default(item) {
return this.$translate(item.translations)
},
},
},
computed: {
myValue: {
get() {
return this.value
},
set(newValue) {
this.$emit('input', newValue)
},
},
},
}
</script>

0 comments on commit b87466e

Please sign in to comment.