добавлен авторищация по api, экспорт и импорт продуктов
This commit is contained in:
parent
c08b36d1b6
commit
af1e381ca0
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('api_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('user_id')->nullable()->unsigned();
|
||||
$table->string('token');
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('api_tokens');
|
||||
}
|
||||
};
|
13
Modules/Api/Entities/ApiToken.php
Normal file
13
Modules/Api/Entities/ApiToken.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Entities;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Modules\Support\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ApiToken extends Model{
|
||||
protected $fillable = ['user_id', 'token'];
|
||||
|
||||
public $timestamps = false;
|
||||
}
|
46
Modules/Api/Http/Controllers/ApiAuthController.php
Normal file
46
Modules/Api/Http/Controllers/ApiAuthController.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Api\Entities\ApiToken;
|
||||
use Modules\Api\Http\Requests\ApiAuthRequest;
|
||||
use Modules\User\Entities\User;
|
||||
|
||||
class ApiAuthController extends Controller {
|
||||
|
||||
public function login(ApiAuthRequest $request)
|
||||
{
|
||||
$user = User::findByEmail($request->email);
|
||||
|
||||
if (Auth::guard()->attempt($request->validated()) && $user->hasRoleName('admin')) {
|
||||
$apiToken = ApiToken::create([
|
||||
'token' => 'apit_' . Str::random() . md5(Carbon::now()->toString()),
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
return response([
|
||||
'token' => $apiToken,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'Unauthorized.'
|
||||
], 401);
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
$token = request()->header('authorization');
|
||||
|
||||
ApiToken::query()
|
||||
->where('token', $token)
|
||||
->delete();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success'
|
||||
]);
|
||||
}
|
||||
}
|
16
Modules/Api/Http/Controllers/ExportProductController.php
Normal file
16
Modules/Api/Http/Controllers/ExportProductController.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Modules\Product\Entities\Product;
|
||||
|
||||
class ExportProductController extends Controller {
|
||||
|
||||
public function __invoke()
|
||||
{
|
||||
$products = Product::with('categories')->get();
|
||||
return response()->json($products);
|
||||
}
|
||||
}
|
171
Modules/Api/Http/Controllers/ImportProductController.php
Normal file
171
Modules/Api/Http/Controllers/ImportProductController.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Api\Http\Requests\ImportProductRequest;
|
||||
use Modules\Brand\Entities\Brand;
|
||||
use Modules\Brand\Entities\BrandTranslation;
|
||||
use Modules\Category\Entities\Category;
|
||||
use Modules\Category\Entities\CategoryTranslation;
|
||||
use Modules\Media\Entities\File;
|
||||
use Modules\Meta\Entities\MetaData;
|
||||
use Modules\Meta\Entities\MetaDataTranslation;
|
||||
use Modules\Product\Entities\Product;
|
||||
|
||||
class ImportProductController extends Controller {
|
||||
public function __invoke(ImportProductRequest $request)
|
||||
{
|
||||
foreach ($request->data as $data) {
|
||||
|
||||
$file = $this->getFilesFromUrl($data['image']);
|
||||
|
||||
$product = Product::create($data + [
|
||||
'brand_id' => $this->getBrandId($data['brand']),
|
||||
]) ;
|
||||
|
||||
$this->createMeta($data['seo'] ?? [], Product::class, $product->id);
|
||||
|
||||
$product->files()->attach([$file->id => ['zone' => 'base_image']]);
|
||||
$product->categories()->attach($this->getCategories($data['categories']));
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success'
|
||||
]);
|
||||
}
|
||||
|
||||
private function getCategories($productCategories) {
|
||||
$categories = [];
|
||||
|
||||
foreach ($productCategories as $productCategory) {
|
||||
$parendId = null;
|
||||
foreach ($productCategory as $category) {
|
||||
$parendId = $this->getCategoryFromName($category, $parendId);
|
||||
}
|
||||
$categories[] = $parendId;
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
private function getCategoryFromName($data, $parendId) {
|
||||
$category = CategoryTranslation::where([
|
||||
'name' => $data['name'],
|
||||
'locale' => 'en'
|
||||
])->first();
|
||||
|
||||
if (!empty($category)) {
|
||||
return $category->category_id;
|
||||
}
|
||||
|
||||
$category = new Category();
|
||||
|
||||
$category->name = $data['name'];
|
||||
$category->is_active = true;
|
||||
$category->is_searchable = true;
|
||||
$category->parent_id = $parendId;
|
||||
|
||||
$category->save();
|
||||
|
||||
if (isset($data['image']['logo']) && !empty($data['image']['logo'])) {
|
||||
$this->createLogo($data['image']['logo'], $category, Category::class);
|
||||
}
|
||||
|
||||
if (isset($data['image']['banner']) && !empty($data['image']['banner'])) {
|
||||
$this->createBanner($data['image']['banner'], $category, Category::class);
|
||||
}
|
||||
|
||||
return $category->id;
|
||||
}
|
||||
|
||||
private function getBrandId($data) {
|
||||
// dd($data);
|
||||
|
||||
$brand = BrandTranslation::where([
|
||||
'name' => $data['name'],
|
||||
'locale' => 'en'
|
||||
])->first();
|
||||
|
||||
if (!empty($brand)) {
|
||||
return $brand->brand_id;
|
||||
}
|
||||
|
||||
$brand = new Brand();
|
||||
$brand->name = $data['name'];
|
||||
$brand->is_active = true;
|
||||
$brand->save();
|
||||
|
||||
$this->createMeta($data['seo'] ?? [], Brand::class, $brand->id);
|
||||
|
||||
if (isset($data['image']['logo']) && !empty($data['image']['logo'])) {
|
||||
$this->createLogo($data['image']['logo'], $brand, Brand::class);
|
||||
}
|
||||
|
||||
if (isset($data['image']['banner']) && !empty($data['image']['banner'])) {
|
||||
$this->createBanner($data['image']['banner'], $brand, Brand::class);
|
||||
}
|
||||
|
||||
return $brand->id;
|
||||
}
|
||||
|
||||
private function getFilesFromUrl($imageUrl) {
|
||||
$headers = get_headers($imageUrl, 1);
|
||||
$fileContents = file_get_contents($imageUrl);
|
||||
$path = 'media/' . time() . Str::random(6) . '.' . pathinfo($imageUrl, PATHINFO_EXTENSION);
|
||||
|
||||
Storage::put($path, $fileContents);
|
||||
|
||||
return File::create([
|
||||
'user_id' => 1,
|
||||
'disk' => config('filesystems.default'),
|
||||
'filename' => pathinfo($imageUrl, PATHINFO_BASENAME),
|
||||
'path' => $path,
|
||||
'extension' => pathinfo($imageUrl, PATHINFO_EXTENSION) ?? '',
|
||||
'mime' => $headers['Content-Type'],
|
||||
'size' => strlen($fileContents),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createMeta($seo, $class, $id) {
|
||||
$metaData = new MetaDataTranslation();
|
||||
|
||||
$metaData->meta_title = isset($seo['title']) ? $seo['title'] : '';
|
||||
$metaData->meta_description = isset($seo['description']) ? $seo['description'] : '';
|
||||
$metaData->locale = 'en';
|
||||
$metaData->meta_data_id = $this->getMetaId($class, $id);
|
||||
|
||||
$metaData->save();
|
||||
}
|
||||
|
||||
private function getMetaId($class, $id) {
|
||||
$meta = MetaData::where([
|
||||
'entity_type' => $class,
|
||||
'entity_id' => $id,
|
||||
])->first();
|
||||
|
||||
if (!empty($meta)) {
|
||||
return $meta->id;
|
||||
}
|
||||
|
||||
$meta = new MetaData();
|
||||
$meta->entity_type = $class;
|
||||
$meta->entity_id = $id;
|
||||
$meta->save();
|
||||
|
||||
return $meta->id;
|
||||
}
|
||||
|
||||
private function createLogo($url, $class, $className) {
|
||||
$file = $this->getFilesFromUrl($url);
|
||||
$class->files()->attach([$file->id => ['zone' => 'logo']]);
|
||||
}
|
||||
|
||||
private function createBanner($url, $class, $className) {
|
||||
$file = $this->getFilesFromUrl($url);
|
||||
$class->files()->attach([$file->id => ['zone' => 'banner']]);
|
||||
}
|
||||
}
|
54
Modules/Api/Http/Middleware/ApiAuthorization.php
Normal file
54
Modules/Api/Http/Middleware/ApiAuthorization.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Middleware;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Api\Entities\ApiToken;
|
||||
|
||||
class ApiAuthorization
|
||||
{
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string $permission
|
||||
* @param string $to
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($this->hasAccess($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
return $this->handleUnauthorizedRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $permission
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
private function handleUnauthorizedRequest(Request $request)
|
||||
{
|
||||
return response()->json([
|
||||
'status' => 'Unauthorized.'
|
||||
], 401);
|
||||
}
|
||||
|
||||
private function hasAccess(Request $request)
|
||||
{
|
||||
$token = $request->header('authorization');
|
||||
|
||||
$hasToken = ApiToken::query()
|
||||
->where('token', $token)
|
||||
->exists();
|
||||
|
||||
if (!empty($token) && $hasToken) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
23
Modules/Api/Http/Requests/ApiAuthRequest.php
Normal file
23
Modules/Api/Http/Requests/ApiAuthRequest.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Requests;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Core\Http\Requests\Request;
|
||||
|
||||
class ApiAuthRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'email', Rule::exists('users', 'email')],
|
||||
'password' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
69
Modules/Api/Http/Requests/ImportProductRequest.php
Normal file
69
Modules/Api/Http/Requests/ImportProductRequest.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Http\Requests;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Core\Http\Requests\Request;
|
||||
|
||||
class ImportProductRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Available attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $availableAttributes = 'product::attributes';
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
// 'slug' => $this->getSlugRules(),
|
||||
'data' => 'required|array|min:1',
|
||||
'data.*.slug' => 'sometimes',
|
||||
'data.*.name' => 'required',
|
||||
'data.*.description' => 'required',
|
||||
|
||||
'data.*.brand' => 'required|array|min:1',
|
||||
'data.*.brand.name' => 'required|string',
|
||||
'data.*.brand.seo' => 'sometimes',
|
||||
'data.*.brand.image' => 'sometimes',
|
||||
|
||||
'data.*.tax_class_id' => ['nullable', Rule::exists('tax_classes', 'id')],
|
||||
'data.*.is_virtual' => 'required|boolean',
|
||||
'data.*.is_active' => 'required|boolean',
|
||||
'data.*.price' => 'required|numeric|min:0|max:99999999999999',
|
||||
'data.*.special_price' => 'nullable|numeric|min:0|max:99999999999999',
|
||||
'data.*.special_price_type' => ['nullable', Rule::in(['fixed', 'percent'])],
|
||||
'data.*.special_price_start' => 'nullable|date',
|
||||
'data.*.special_price_end' => 'nullable|date',
|
||||
'data.*.manage_stock' => 'required|boolean',
|
||||
'data.*.qty' => 'required_if:manage_stock,1|nullable|numeric',
|
||||
'data.*.in_stock' => 'required|boolean',
|
||||
'data.*.new_from' => 'nullable|date',
|
||||
'data.*.new_to' => 'nullable|date',
|
||||
'data.*.categories' => 'required|array|min:1',
|
||||
'data.*.categories.*.*.name' => 'required|string',
|
||||
'data.*.categories.*.*.seo' => 'sometimes',
|
||||
'data.*.image' => 'required|string',
|
||||
];
|
||||
}
|
||||
|
||||
// private function getSlugRules()
|
||||
// {
|
||||
// $rules = $this->route()->getName() === 'admin.products.update' ? ['required'] : ['sometimes'];
|
||||
|
||||
// $slug = Product::withoutGlobalScope('active')
|
||||
// ->where('id', $this->id)
|
||||
// ->value('slug');
|
||||
|
||||
// $rules[] = Rule::unique('products', 'slug')->ignore($slug, 'slug');
|
||||
|
||||
// return $rules;
|
||||
// }
|
||||
}
|
18
Modules/Api/Providers/ApiProvider.php
Normal file
18
Modules/Api/Providers/ApiProvider.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Api\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ApiProvider extends ServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->loadRoutesFrom(__DIR__ . '/../Routes/admin.php');
|
||||
$this->loadRoutesFrom(__DIR__ . '/../Routes/public.php');
|
||||
}
|
||||
}
|
9
Modules/Api/Routes/admin.php
Normal file
9
Modules/Api/Routes/admin.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Api\Http\Controllers\ApiAuthController;
|
||||
|
||||
Route::prefix('api')->group(function () {
|
||||
Route::post('/login', [ApiAuthController::class, 'login'])->name('api.auth.login');
|
||||
Route::post('/logout', [ApiAuthController::class, 'logout'])->middleware('api_auth')->name('api.auth.logout');
|
||||
});
|
14
Modules/Api/Routes/public.php
Normal file
14
Modules/Api/Routes/public.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Api\Http\Controllers\ExportProductController;
|
||||
use Modules\Api\Http\Controllers\ImportProductController;
|
||||
|
||||
Route::group(['prefix' => 'export', 'middleware' => 'api_auth'], function () {
|
||||
Route::get('products', ExportProductController::class)->name('api.export.products');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'import', 'middleware' => 'api_auth'], function () {
|
||||
Route::post('products', ImportProductController::class)->name('api.import.products');
|
||||
});
|
||||
|
3
Modules/Api/Simple/import.json
Normal file
3
Modules/Api/Simple/import.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ class CoreServiceProvider extends ServiceProvider
|
||||
'localize' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
|
||||
'locale_session_redirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
|
||||
'localization_redirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
|
||||
'api_auth' => \Modules\Api\Http\Middleware\ApiAuthorization::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -181,6 +181,7 @@ return [
|
||||
FleetCart\Providers\RouteServiceProvider::class,
|
||||
Nwidart\Modules\LaravelModulesServiceProvider::class,
|
||||
Mehedi\Stylist\StylistServiceProvider::class,
|
||||
Modules\Api\Providers\ApiProvider::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user