baks-dev/four-tochki-products
Laravel/PHP 8.4+ модуль интеграции с 4tochki: импорт и синхронизация каталога продукции, работа со складами партнёра (идентификаторы через b2b.4tochki.ru). Установка через Composer, assets:install, миграции Doctrine; есть PHPUnit-тесты.
Install the package via Composer:
composer require baks-dev/four-tochki-products
Publish configuration and assets (required for API keys, warehouse IDs, and DB schema):
php artisan baks:assets:install
config/four-tochki.php) for API credentials and warehouse IDs.Configure your 4tochki credentials:
config/four-tochki.php with:
api_key (from 4tochki B2B).warehouse_ids (array of partner warehouse IDs).default_warehouse (for fallback logic).Run migrations (adapt for Laravel):
php artisan doctrine:migrations:diff
php artisan doctrine:migrations:migrate
First sync test:
use BaksDev\FourTochki\Product\ProductService;
$service = app(ProductService::class);
$products = $service->fetchAll(); // Fetches from 4tochki API
$service->syncToLocal(); // Updates local DB (if configured)
Goal: Sync a subset of 4tochki products to your Laravel app for a marketplace.
$products = $service->fetchByCategory('electronics', ['limit' => 50]);
Product model:
foreach ($products as $remoteProduct) {
$localProduct = \App\Models\Product::updateOrCreate(
['sku' => $remoteProduct->getSku()],
[
'name' => $remoteProduct->getName(),
'price' => $remoteProduct->getPrice()->getAmount(),
'warehouse_id' => $remoteProduct->getWarehouseId(),
'attributes' => $remoteProduct->getAttributes()->toArray(),
]
);
}
$remoteProduct->getImages()->each(function ($image) use ($localProduct) {
$localProduct->addMediaFromUrl($image->getUrl())->toMediaCollection('4tochki_images');
});
config/four-tochki.php (API keys, warehouse IDs).src/Product/ProductService.php (core logic for fetching/syncing).src/Entity/Product.php (Doctrine models; extend for Laravel).src/Command/ (e.g., SyncProductsCommand for cron jobs).tests/Unit/ (if any; otherwise, start with API contract tests).Pattern: Use the bundle in a Symfony microservice alongside Laravel, communicating via API.
4tochki-service).POST /api/products/syncGET /api/warehouses/{id}/stock$response = Http::post('http://4tochki-service/api/products/sync', [
'category' => 'electronics',
]);
Alternative: Containerize the bundle in Laravel using symfony/console bridge:
// In Laravel service provider
$this->app->singleton('command.baks.install', function ($app) {
return new \BaksDev\FourTochki\Command\InstallAssetsCommand();
});
Pattern: Trigger actions on 4tochki API events (e.g., stock updates, order changes).
ProductUpdated events to update Laravel cache.
use BaksDev\FourTochki\Event\ProductUpdated;
event(new ProductUpdated($remoteProduct));
// In EventServiceProvider
protected $listen = [
ProductUpdated::class => [
\App\Listeners\UpdateLocalProduct::class,
],
];
Use Case: Real-time inventory updates without polling.
Pattern: Route products/orders to the correct 4tochki warehouse.
$warehouseId = app(\BaksDev\FourTochki\Warehouse\WarehouseSelector::class)
->selectByRegion($customer->region);
$product->setWarehouseId($warehouseId);
Extension: Create a strategy pattern for warehouse selection:
interface WarehouseStrategy {
public function select(array $context): string;
}
class RegionBasedStrategy implements WarehouseStrategy {
public function select(array $context): string {
return WarehouseRepository::findByRegion($context['region']);
}
}
Pattern: Use chunking for large product catalogs.
$service = app(ProductService::class);
$service->syncInBatches(1000); // 1000 products per batch
foreach ($products as $batch) {
SyncProductsJob::dispatch($batch)->delay(now()->addMinutes(1));
}
Pattern: Map 4tochki’s dynamic attributes to Laravel’s JSON fields.
json column:
$localProduct->attributes = json_encode([
'brand' => $remoteProduct->getAttribute('brand'),
'weight_kg' => $remoteProduct->getAttribute('weight')->getValue(),
]);
public function getBrandAttribute() {
return $this->attributes['brand'] ?? null;
}
Pattern: Contract tests for API responses.
public function testProductSyncReturnsExpectedStructure() {
$response = $this->call('POST', '/api/products/sync', ['category' => 'books']);
$response->assertJsonStructure([
'data' => [
'*' => [
'id', 'sku', 'name', 'price', 'warehouse_id',
'attributes' => ['*'],
'images' => ['*'],
],
],
]);
}
Doctrine vs. Eloquent Conflicts:
use Doctrine\ORM\Mapping as ORM;
use Illuminate\Database\Eloquent\Model;
#[ORM\Entity]
class Product extends Model {
// Hybrid mapping
}
laravel-doctrine/orm bridge for partial compatibility.API Rate Limits:
use Symfony\Component\HttpClient\RetryableHttpClient;
$client = new RetryableHttpClient(
new \Symfony\Contracts\HttpClient\HttpClient(),
[
'max_retries' => 3,
'delay' => 1000, // 1 second
]
);
Warehouse ID Mismatches:
$warehouses = $service->fetchWarehouses();
config(['four-tochki.warehouse_ids' => $warehouses->pluck('id')]);
Timezone/Date Handling:
config(['four-tochki.datetime_format' => 'Y-m-d\TH:i:s\Z']);
How can I help you explore Laravel packages today?