Skip to content

Commit

Permalink
Обновление моделей товаров, импорт из таблиц MODX, доработка админки
Browse files Browse the repository at this point in the history
  • Loading branch information
bezumkin committed Aug 18, 2023
1 parent 88dcd26 commit edfe813
Show file tree
Hide file tree
Showing 20 changed files with 657 additions and 107 deletions.
49 changes: 49 additions & 0 deletions core/db/seeds/ProductFiles.php
@@ -0,0 +1,49 @@
<?php

use App\Models\File;
use App\Models\Product;
use Phinx\Seed\AbstractSeed;
use Slim\Psr7\UploadedFile;
use Vesp\Services\Eloquent;

class ProductFiles extends AbstractSeed
{

public function getDependencies(): array
{
return ['Categories', 'Products'];
}

public function run(): void
{
$pdo = (new Eloquent())->getDatabaseManager()->getPdo();
$stmt = $pdo->prepare(
'SELECT * FROM `modx_ms2_product_files` WHERE `product_id` = :remote_id AND `parent` = 0'
);

/** @var Product $product */
foreach (Product::query()->cursor() as $product) {
$stmt->execute([':remote_id' => $product->remote_id]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Check if File already uploaded
if ($product->productFiles()->where('remote_id', $row['id'])->count()) {
continue;
}

// Upload new
$filename = tempnam(getenv('UPLOAD_DIR'), 'img_');
file_put_contents($filename, file_get_contents('https://i.pravatar.cc/500'));
$data = new UploadedFile($filename, $row['name'] ?? basename($filename), mime_content_type($filename));
$file = new File();
$file->uploadFile($data);
$product->productFiles()->insert([
'product_id' => $product->id,
'file_id' => $file->id,
'rank' => $row['rank'],
'remote_id' => $row['id'],
]);
unlink($filename);
}
}
}
}
29 changes: 29 additions & 0 deletions core/db/seeds/ProductLinks.php
@@ -0,0 +1,29 @@
<?php

use App\Models\Product;
use App\Models\ProductLink;
use Phinx\Seed\AbstractSeed;
use Vesp\Services\Eloquent;

class ProductLinks extends AbstractSeed
{
public function getDependencies(): array
{
return ['Products'];
}

public function run(): void
{
$pdo = (new Eloquent())->getDatabaseManager()->getPdo();
$stmt = $pdo->query("SELECT * FROM `modx_ms2_product_links`");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
/** @var Product $product */
$product = Product::query()->where('remote_id', $row['master'])->first();
/** @var Product $link */
$link = Product::query()->where('remote_id', $row['slave'])->first();
if ($product && $link) {
ProductLink::query()->updateOrInsert(['product_id' => $product->id, 'link_id' => $link->id]);
}
}
}
}
182 changes: 157 additions & 25 deletions core/db/seeds/Products.php
Expand Up @@ -7,40 +7,172 @@

class Products extends AbstractSeed
{
protected $categories = 100;
protected $products = 1000;

public function getDependencies(): array
{
return ['Languages', 'Categories'];
}

public function run(): void
{
if (!class_exists('Faker\Factory')) {
return;
$categories = [];
/** @var Category $category */
foreach (Category::query()->select('id', 'remote_id')->cursor() as $category) {
$categories[$category->remote_id] = $category->id;
}

(new Eloquent())->getConnection()->getSchemaBuilder()->disableForeignKeyConstraints();
Category::query()->truncate();
Product::query()->truncate();
$pdo = (new Eloquent())->getDatabaseManager()->getPdo();
$stmt = $pdo->query(
"SELECT `r`.*, `d`.*, GROUP_CONCAT(`c`.`category_id` SEPARATOR ',') AS `categories`,
`ln`.`pagetitle` as `pagetitle_fr`, `ln`.`longtitle` as `longtitle_fr`, `ln`.`description` as `description_fr`, `ln`.`content` as `content_fr`
FROM `modx_site_content` `r`
LEFT JOIN `modx_ms2_products` `d` ON `r`.`id` = `d`.`id`
LEFT JOIN `modx_ms2_product_categories` `c` ON `r`.`id` = `c`.`product_id`
LEFT JOIN `modx_lingua_site_content` `ln` ON `ln`.`resource_id` = `r`.`id`
WHERE `class_key` = 'msProduct'
GROUP BY `r`.`id`
ORDER BY `r`.`parent`,`r`.`id` ASC"
);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if (!$product = Product::query()->where('remote_id', $row['id'])->first()) {
$product = new Product();
$product->remote_id = $row['id'];
}
if (empty($categories[$row['parent']])) {
throw new RuntimeException('Could not find product primary category ' . print_r($row, true));
}
$product->category_id = $categories[$row['parent']];
$product->alias = trim($row['alias']);
$product->uri = preg_replace('#^shop/#', '', trim($row['uri'], '/'));

$product->price = $row['price'] ?? 0;
$product->old_price = $row['old_price'] ?? 0;
$product->article = $row['article'] ?? null;
$product->weight = $row['weight'] ?? 0;

$product->new = !empty($row['new']);
$product->popular = !empty($row['popular']);
$product->favorite = !empty($row['favorite']);

$product->made_in = !empty($row['made_in']) ? trim($row['made_in']) : null;
$product->colors = $row['color'] && $row['color'] !== '[""]' ? json_decode($row['color'], true) : null;

// Variants
if ($de = $row['variants'] && $row['variants'] !== '[""]' ? json_decode($row['variants'], true) : null) {
$de = array_filter($de);
}
if (!$de && $fr = $row['variants_fr'] && $row['variants_fr'] !== '[""]' ? json_decode($row['variants_fr'], true) : null) {
if ($fr = array_filter($fr)) {
$de = [];
foreach ($fr as $tmp) {
$de[] = self::translateVariants($tmp, 'fr');
}
}
}
if ($de) {
$product->variants = $de;
}

$product->active = empty($row['deleted']) && !empty($row['published']);
$product->rank = $row['menuindex'];
$product->created_at = $row['createdon'];
$product->updated_at = $row['editedon'] ?: null;
$product->save();

$faker = Faker\Factory::create();
$product->productCategories()->delete();
if (!empty($row['categories'])) {
$tmp = explode(',', $row['categories']);
foreach ($tmp as $id) {
if (isset($categories[$id]) && (int)$categories[$id] !== $product->category_id) {
$product->productCategories()->insert(
['product_id' => $product->id, 'category_id' => $categories[$id]]
);
}
}
}

for ($i = 0; $i < $this->categories; $i++) {
Category::query()->create([
'title' => 'Категория ' . ($i + 1),
'alias' => 'category-' . ($i + 1),
'description' => $faker->text,
'active' => random_int(0, 9) > 0,
$product->translations()->updateOrCreate(['lang' => 'ru'], [
'title' => trim($row['pagetitle']) ?: null,
'subtitle' => trim($row['longtitle']) ?: null,
'description' => trim($row['description']) ?: null,
'content' => trim($row['content']) ?: null,
]);
$product->translations()->updateOrCreate(['lang' => 'en'], [
'title' => trim($row['pagetitle_fr']) ?: null,
'subtitle' => trim($row['longtitle_fr']) ?: null,
'description' => trim($row['description_fr']) ?: null,
'content' => trim($row['content_fr']) ?: null,
]);
}
}

for ($i = 0; $i < $this->products; $i++) {
Product::query()->create([
'title' => 'Товар ' . ($i + 1),
'description' => $faker->text,
'alias' => 'product-' . ($i + 1),
'category_id' => random_int(1, $this->categories),
'sku' => $faker->unique()->randomNumber(9, true),
'price' => $faker->randomFloat(2, 100, 10000),
'active' => random_int(0, 9) > 0,
]);
public static function translateVariants(string $key, string $fromLang = 'de'): string
{
$de = [
'Reinschnitt',
'Reinschnitt (1.5mm)',
'Mittelschnitt',
'Mittelschnitt (3.5mm)',
'Grobschnitt',
'Grobschnitt (9mm)',
'Mittel-Grob',
'apfel',
'aprikose',
'banane',
'kirsche',
'mango',
'mojito',
'pfirsich',
'gerade',
'gebogen',
'gerade',
'Quadrate',
'frankreich',
'schwarz',
'Deutschland',
'Spanien',
'Italien',
'Kuh',
'Lines',
];
$fr = [
'Moins fine',
'Moins fine (1.5mm)',
'Moyenne',
'Moyenne (3.5mm)',
'Grossière',
'Grossière (9mm)',
'Moyenne-Grossiè',
'pomme',
'apricot',
'banane',
'cherry',
'mangue',
'mojito',
'peach',
'droite',
'courbee',
'droit',
'Carré',
'france',
'black',
'allemagne',
'espagne',
'italie',
'Vache',
'Lines',
];

if ($key === 'courbée') {
$key = 'courbee';
}

$tmp = array_map('strtolower', $fromLang === 'de' ? $de : $fr);
$idx = array_search(strtolower($key), $tmp, true);
if ($idx === false) {
return $key;
}

return $fromLang === 'de' ? $fr[$idx] : $de[$idx];
}
}
}
35 changes: 31 additions & 4 deletions core/src/Controllers/Admin/Products.php
Expand Up @@ -2,6 +2,7 @@

namespace App\Controllers\Admin;

use App\Controllers\Traits\TranslateModelController;
use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -10,25 +11,51 @@

class Products extends ModelController
{
use TranslateModelController;

protected $scope = 'products';
protected $model = Product::class;

protected function beforeGet(Builder $c): Builder
{
return $c->with('translations');
}

protected function beforeCount(Builder $c): Builder
{
if ($query = $this->getProperty('query')) {
$c->where(static function (Builder $c) use ($query) {
if ($category = (int)$this->getProperty('category')) {
$c->where(static function (Builder $c) use ($category) {
$c->where('category_id', $category);
$c->orWhereHas('productCategories', static function (Builder $c) use ($category) {
$c->where('category_id', $category);
});
});
}
if ($query = trim($this->getProperty('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%");
});
}
if ($exclude = $this->getProperty('exclude')) {
if (!is_array($exclude)) {
$exclude = explode(',', $exclude);
}
$c->whereNotIn('id', array_map('intval', $exclude));
}

return $c;
}

protected function afterCount(Builder $c): Builder
{
$c->with('category:id,title');
$c->with('firstFile');
$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;
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/Controllers/Web/Category/Products.php
Expand Up @@ -14,7 +14,7 @@ class Products extends \App\Controllers\Web\Products

public function checkScope(string $method): ?ResponseInterface
{
$c = Category::query()->where(['active' => true, 'alias' => $this->getProperty('category')]);
$c = Category::query()->where(['active' => true, 'uri' => $this->getProperty('category')]);
if (!$this->category = $c->first()) {
return $this->failure('', 404);
}
Expand All @@ -25,7 +25,7 @@ public function checkScope(string $method): ?ResponseInterface
protected function beforeGet(Builder $c): Builder
{
$c->where('active', true);
$c->with('category:id,alias,title');
$c->with('category:id,uri,active', 'category.translations:category_id,lang,title');
$c->with('productFiles', static function (HasMany $c) {
$c->where('active', true);
$c->orderBy('rank');
Expand Down
2 changes: 1 addition & 1 deletion core/src/Controllers/Web/Products.php
Expand Up @@ -27,7 +27,7 @@ protected function beforeCount(Builder $c): Builder

protected function afterCount(Builder $c): Builder
{
$c->with('category:id,title,alias');
$c->with('category:id,uri,active', 'category.translations:category_id,lang,title');
$c->with('firstFile');

return $c;
Expand Down

0 comments on commit edfe813

Please sign in to comment.