[CLEAR-42] adding recipes

This commit is contained in:
Artur Nowakowski 2019-12-15 15:45:22 +01:00
parent a62b3b5868
commit ef20883542
16 changed files with 356 additions and 8 deletions

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\AddRecipeRequest;
use App\Repositories\RecipesRepositoryInterface;
use Illuminate\Http\Request;
class RecipesController extends Controller
{
private $recipesRepository;
private $paginationSize = 10;
public function __construct(RecipesRepositoryInterface $recipesRepository)
{
$this->recipesRepository = $recipesRepository;
}
public function add(AddRecipeRequest $request)
{
$recipe = $this->recipesRepository->create(collect($request->all()));
return response()->json(['success' => true, 'data' => ['recipe' => $recipe]], 200);
}
public function index(Request $request)
{
$recipes = $this->recipesRepository->paginate($this->paginationSize);
return response()->json(['success' => true, 'data' => ['recipes' => $recipes]], 200);
}
}

View File

@ -25,6 +25,10 @@ class AddProductRequest extends FormRequest
{ {
return [ return [
'name' => 'required', 'name' => 'required',
'kcal' => 'required',
'carbohydrates' => 'required',
'protein' => 'required',
'fat' => 'required',
]; ];
} }
@ -32,6 +36,10 @@ class AddProductRequest extends FormRequest
{ {
return [ return [
'name.required' => 'Product name is required', 'name.required' => 'Product name is required',
'kcal.required' => 'Kcal are required',
'carbohydrates.required' => 'Carbohydrates are required',
'protein.required' => 'Protein are required',
'fat.required' => 'Fat is required',
]; ];
} }
} }

View File

@ -0,0 +1,41 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AddRecipeRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'steps' => 'required',
'ingredients' => 'required',
];
}
public function messages()
{
return [
'name.required' => 'Product name is required',
'steps.required' => 'Recipe steps are required',
'ingredients.required' => 'Recipe ingredients are required',
];
}
}

25
api/app/Models/Recipe.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App;
use App\Models\Product;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
protected $guarded = ['id'];
protected $with = ['steps', 'ingredients'];
protected $hidden = ['created_at', 'updated_at'];
public function steps()
{
return $this->hasMany(RecipeStep::class);
}
public function ingredients()
{
return $this->hasMany(RecipeProduct::class);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App;
use App\Models\Product;
use Illuminate\Database\Eloquent\Model;
use App\Traits\HasCompositeKey;
class RecipeProduct extends Model
{
use HasCompositeKey;
protected $primaryKey = ['recipe_id', 'product_id'];
protected $with = ['product'];
public $incrementing = false;
public $timestamps = false;
protected $table = 'recipe_products';
protected $guarded = ['id'];
protected $hidden = ['recipe_id', 'product_id'];
public function product()
{
return $this->belongsTo(Product::class);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class RecipeStep extends Model
{
protected $guarded = ['id'];
protected $hidden = ['recipe_id'];
public $timestamps = false;
}

View File

@ -24,6 +24,5 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
Schema::defaultStringLength(191);
} }
} }

View File

@ -4,6 +4,8 @@ namespace App\Providers;
use App\Repositories\ProductsRepository; use App\Repositories\ProductsRepository;
use App\Repositories\ProductsRepositoryInterface; use App\Repositories\ProductsRepositoryInterface;
use App\Repositories\RecipesRepository;
use App\Repositories\RecipesRepositoryInterface;
use App\Repositories\UsersRepository; use App\Repositories\UsersRepository;
use App\Repositories\UsersRepositoryInterface; use App\Repositories\UsersRepositoryInterface;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -29,5 +31,6 @@ class RepositoriesServiceProvider extends ServiceProvider
{ {
$this->app->bind(UsersRepositoryInterface::class, UsersRepository::class); $this->app->bind(UsersRepositoryInterface::class, UsersRepository::class);
$this->app->bind(ProductsRepositoryInterface::class, ProductsRepository::class); $this->app->bind(ProductsRepositoryInterface::class, ProductsRepository::class);
$this->app->bind(RecipesRepositoryInterface::class, RecipesRepository::class);
} }
} }

View File

@ -0,0 +1,64 @@
<?php
namespace App\Repositories;
use App\Recipe;
use App\RecipeProduct;
use App\RecipeStep;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class RecipesRepository implements RecipesRepositoryInterface
{
public function create(Collection $data)
{
return DB::transaction(function () use ($data) {
$recipeData = $data->only(['name', 'description']);
$ingredients = $data->get('ingredients');
$steps = $data->get('steps');
$recipe = Recipe::create($recipeData->toArray());
foreach ($ingredients as $ingredient) {
RecipeProduct::create([
'recipe_id' => $recipe['id'],
'product_id' => $ingredient['id'],
'weight' => $ingredient['weight']
]);
}
foreach ($steps as $step) {
RecipeStep::create(['recipe_id' => $recipe['id'], 'step' => $step]);
}
return $this->get($recipe['id']);
});
}
public function get(int $id)
{
$rawRecipe = Recipe::where('id', $id)->first();
$recipe = [
'name' => $rawRecipe['name'],
'description' => $rawRecipe['description'],
'ingredients' => $this->processIngredients($rawRecipe['ingredients']),
'steps' => $rawRecipe['steps']->pluck('step')
];
return $recipe;
}
public function processIngredients(Collection $ingredients)
{
return $ingredients->map(function ($item) {
return collect(array_merge($item->toArray(), $item['product']->toArray()))->only(['name', 'weight']);
});
}
public function paginate($chunkSize)
{
return Recipe::paginate($chunkSize);
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Repositories;
use Illuminate\Support\Collection;
interface RecipesRepositoryInterface
{
public function create(Collection $data);
public function paginate(int $chunkSize);
}

View File

@ -15,7 +15,7 @@ class CreateUsersTable extends Migration
public function up() public function up()
{ {
Schema::create('users', function (Blueprint $table) { Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id'); $table->increments('id');
$table->string('email')->unique(); $table->string('email')->unique();
$table->string('password'); $table->string('password');
$table->integer('role')->default(UserRole::USER); $table->integer('role')->default(UserRole::USER);

View File

@ -14,12 +14,12 @@ class CreateProductsTable extends Migration
public function up() public function up()
{ {
Schema::create('products', function (Blueprint $table) { Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id'); $table->increments('id');
$table->string('name')->unique(); $table->string('name')->unique();
$table->integer('kcal')->nullable(); $table->integer('kcal')->unsigned();
$table->float('carbohydrates')->nullable(); $table->float('carbohydrates');
$table->float('protein')->nullable(); $table->float('protein');
$table->float('fat')->nullable(); $table->float('fat');
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRecipesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('recipes', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('recipes');
}
}

View File

@ -0,0 +1,43 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRecipeProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('recipe_products', function (Blueprint $table) {
$table->integer('recipe_id')->unsigned();
$table->integer('product_id')->unsigned();
$table->integer('weight')->unsigned();
$table->foreign('recipe_id')
->references('id')
->on('recipes')
->onUpdate('cascade')
->onDelete('cascade');
$table->foreign('product_id')
->references('id')
->on('products')
->onUpdate('cascade')
->onDelete('cascade');
$table->primary(['recipe_id', 'product_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('recipe_products');
}
}

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRecipeStepsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('recipe_steps', function (Blueprint $table) {
$table->increments('id');
$table->integer('recipe_id')->unsigned();
$table->string('step');
$table->foreign('recipe_id')
->references('id')
->on('recipes')
->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('recipe_steps');
}
}

View File

@ -9,6 +9,11 @@ Route::group(['prefix' => 'user', 'middleware' => ['assign.guard:users']], funct
}); });
Route::prefix('product')->group(function () { Route::prefix('product')->group(function () {
Route::post('/', 'ProductsController@add')->middleware(['assign.guard:users']); Route::post('/', 'ProductsController@add')->middleware(['jwt.auth']);
Route::get('/', 'ProductsController@index'); Route::get('/', 'ProductsController@index');
}); });
Route::prefix('recipe')->group(function () {
Route::post('/', 'RecipesController@add')->middleware(['jwt.auth']);
Route::get('/', 'RecipesController@index');
});