Skip to content

Commit

Permalink
Merge pull request #14 from karlomikus/develop
Browse files Browse the repository at this point in the history
Merge next version
  • Loading branch information
karlomikus committed Nov 11, 2022
2 parents 58ee62e + 97dd37e commit e8431c4
Show file tree
Hide file tree
Showing 46 changed files with 1,121 additions and 111 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# v0.3.0
- Move "Uncategorized" ingredient category to id: 1
- Cocktail save/update methods now save glass type
- Update docker entrypoint with better up/down handling
- Use `laravel/sluggable` package for ingredient and cocktail slug generation
- Added currently used Meilisearch host to user endpoint response
- Update Meilisearch cocktail index settings
- Add `user_id` to cocktail index
- Implement ingredient varieties feature #6
- Increased API request rate limiting
- Add cocktail ingredient substitutes feature
- Add a new cocktail source list
- Exclude optional cocktail ingredients from shelf cocktail matching
- Implement all ingredient category resource endpoints
- Update postman collection
- Implement more tests

# v0.2.0
- Add docker build action
- Add glasses
- Update base cocktail db
- Update error handling
- Add logout

# v0.1.0
- Initial release
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Bar assistant is a self hosted application for managing your home bar. It allows

This repository only contains the API server, if you are looking for easy to use web client, take a look at [Salt Rim](https://github.com/karlomikus/vue-salt-rim).

Note: This application is still in development and there will be breaking changes and loss of data. I do not recommend using this in a "production" environment until a stable version is released.

## Features

- Includes all current IBA cocktails
Expand Down Expand Up @@ -96,7 +98,7 @@ Default login information is:
docker run -d \
-e APP_URL=http://localhost:8080 \
-e MEILISEARCH_HOST=http://localhost:7700 \
-e MEILISEARCH_KEY=TEST \
-e MEILISEARCH_KEY=maskerKey \
kmikus12/bar-assistant-server
```

Expand Down
113 changes: 80 additions & 33 deletions app/Console/Commands/OpenBar.php

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\RecordsNotFoundException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class Handler extends ExceptionHandler
Expand Down Expand Up @@ -56,5 +57,12 @@ public function register()
'message' => $e->getMessage() == "" ? 'Resource record not found.' : $e->getMessage(),
], 404);
});

$this->renderable(function (MethodNotAllowedHttpException $e, $request) {
return response()->json([
'type' => 'api_error',
'message' => $e->getMessage(),
], 405);
});
}
}
8 changes: 7 additions & 1 deletion app/Http/Controllers/CocktailController.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function show(int|string $idOrSlug)
$cocktail = Cocktail::where('id', $idOrSlug)
->orWhere('slug', $idOrSlug)
->firstOrFail()
->load('ingredients.ingredient', 'images', 'tags', 'glass');
->load('ingredients.ingredient', 'images', 'tags', 'glass', 'ingredients.substitutes');

return new CocktailResource($cocktail);
}
Expand All @@ -72,11 +72,14 @@ public function store(CocktailService $cocktailService, CocktailRequest $request
$request->post('source'),
$request->post('images', []),
$request->post('tags', []),
$request->post('glass_id') ? (int) $request->post('glass_id') : null,
);
} catch (Throwable $e) {
abort(500, $e->getMessage());
}

$cocktail->load('ingredients.ingredient', 'images', 'tags', 'glass', 'ingredients.substitutes');

return (new CocktailResource($cocktail))
->response()
->setStatusCode(201)
Expand All @@ -100,11 +103,14 @@ public function update(CocktailService $cocktailService, CocktailRequest $reques
$request->post('source'),
$request->post('images', []),
$request->post('tags', []),
$request->post('glass_id') ? (int) $request->post('glass_id') : null,
);
} catch (Throwable $e) {
abort(500, $e->getMessage());
}

$cocktail->load('ingredients.ingredient', 'images', 'tags', 'glass', 'ingredients.substitutes');

return (new CocktailResource($cocktail))
->response()
->setStatusCode(201)
Expand Down
57 changes: 57 additions & 0 deletions app/Http/Controllers/IngredientCategoryController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);

namespace Kami\Cocktail\Http\Controllers;

use Kami\Cocktail\Models\IngredientCategory;
use Kami\Cocktail\Http\Resources\SuccessActionResource;
use Kami\Cocktail\Http\Requests\IngredientCategoryRequest;
use Kami\Cocktail\Http\Resources\IngredientCategoryResource;

class IngredientCategoryController extends Controller
{
public function index()
{
$categories = IngredientCategory::all();

return IngredientCategoryResource::collection($categories);
}

public function show(int $id)
{
$category = IngredientCategory::findOrFail($id);

return new IngredientCategoryResource($category);
}

public function store(IngredientCategoryRequest $request)
{
$category = new IngredientCategory();
$category->name = $request->post('name');
$category->description = $request->post('description');
$category->save();

return (new IngredientCategoryResource($category))
->response()
->setStatusCode(201);
}

public function update(IngredientCategoryRequest $request, int $id)
{
$category = IngredientCategory::findOrFail($id);
$category->name = $request->post('name');
$category->description = $request->post('description');
$category->save();

return (new IngredientCategoryResource($category))
->response()
->setStatusCode(201);
}

public function delete(int $id)
{
IngredientCategory::findOrFail($id)->delete();

return new SuccessActionResource((object) ['id' => $id]);
}
}
12 changes: 1 addition & 11 deletions app/Http/Controllers/IngredientController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@

use Illuminate\Http\Request;
use Kami\Cocktail\Models\Ingredient;
use Kami\Cocktail\Models\IngredientCategory;
use Kami\Cocktail\Services\IngredientService;
use Kami\Cocktail\Http\Requests\IngredientRequest;
use Kami\Cocktail\Http\Resources\IngredientResource;
use Kami\Cocktail\Http\Resources\SuccessActionResource;
use Kami\Cocktail\Http\Resources\IngredientCategoryResource;

class IngredientController extends Controller
{
Expand All @@ -35,7 +33,7 @@ public function index(Request $request)

public function show(int|string $id)
{
$ingredient = Ingredient::with('cocktails', 'images')
$ingredient = Ingredient::with('cocktails', 'images', 'varieties', 'parentIngredient')
->where('id', $id)
->orWhere('slug', $id)
->firstOrFail();
Expand Down Expand Up @@ -84,12 +82,4 @@ public function delete(int $id)

return new SuccessActionResource((object) ['id' => $id]);
}

public function categories()
{
// TODO MOVE
$categories = IngredientCategory::all();

return IngredientCategoryResource::collection($categories);
}
}
31 changes: 31 additions & 0 deletions app/Http/Requests/IngredientCategoryRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);

namespace Kami\Cocktail\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class IngredientCategoryRequest 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<string, mixed>
*/
public function rules()
{
return [
'name' => 'required',
];
}
}
1 change: 1 addition & 0 deletions app/Http/Resources/CocktailIngredientResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function toArray($request)
'ingredient_id' => $this->ingredient_id,
'name' => $this->ingredient->name,
'ingredient_slug' => $this->ingredient->slug,
'substitutes' => CocktailIngredientSubstituteResource::collection($this->whenLoaded('substitutes')),
];
}
}
27 changes: 27 additions & 0 deletions app/Http/Resources/CocktailIngredientSubstituteResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);

namespace Kami\Cocktail\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \Kami\Cocktail\Models\CocktailIngredientSubstitute
*/
class CocktailIngredientSubstituteResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->ingredient_id,
'slug' => $this->ingredient->slug,
'name' => $this->ingredient->name,
];
}
}
1 change: 1 addition & 0 deletions app/Http/Resources/CocktailResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function toArray($request)
'image_url' => $this->getImageUrl(),
'image_id' => $this->images->first()->id ?? null,
'tags' => $this->tags->pluck('name'),
'user_id' => $this->user_id,
'glass' => new GlassResource($this->whenLoaded('glass')),
'short_ingredients' => $this->ingredients->pluck('ingredient.name'),
'ingredients' => CocktailIngredientResource::collection($this->ingredients),
Expand Down
12 changes: 10 additions & 2 deletions app/Http/Resources/IngredientResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,25 @@ public function toArray($request)
'description' => $this->description,
'origin' => $this->origin,
'image_url' => $this->getImageUrl(),
'parent_ingredient_id' => $this->parent_ingredient_id,
'ingredient_category_id' => $this->ingredient_category_id,
'color' => $this->color,
'category' => new IngredientCategoryResource($this->category),
'cocktails_count' => $this->whenCounted('cocktails'),
'varieties' => $this->when($this->relationLoaded('varieties') || $this->relationLoaded('parentIngredient'), function () {
return $this->getAllRelatedIngredients()->map(function ($v) {
return [
'id' => $v->id,
'slug' => $v->slug,
'name' => $v->name,
];
})->toArray();
}),
'cocktails' => $this->whenLoaded('cocktails', function () {
return $this->cocktails->map(function ($c) {
return [
'id' => $c->id,
'name' => $c->name,
'slug' => $c->slug,
'name' => $c->name,
];
});
})
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/UserResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function toArray($request)
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'search_host' => config('scout.meilisearch.host'),
'search_api_key' => $this->search_api_key,
'favorite_cocktails' => $this->favorites->pluck('cocktail_id'),
'shelf_ingredients' => $this->shelfIngredients->pluck('ingredient_id'),
Expand Down
18 changes: 12 additions & 6 deletions app/Models/Cocktail.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

namespace Kami\Cocktail\Models;

use Illuminate\Support\Str;
use Laravel\Scout\Searchable;
use Spatie\Sluggable\HasSlug;
use Kami\Cocktail\SearchActions;
use Spatie\Sluggable\SlugOptions;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand All @@ -14,17 +15,13 @@

class Cocktail extends Model
{
use HasFactory, Searchable, HasImages;
use HasFactory, Searchable, HasImages, HasSlug;

private $appImagesDir = 'cocktails/';
private $missingImageFileName = 'no-image.jpg'; // TODO: WEBP

protected static function booted()
{
static::saving(function ($cocktail) {
$cocktail->slug = Str::slug($cocktail->name);
});

static::saved(function($cocktail) {
SearchActions::update($cocktail);
});
Expand All @@ -34,6 +31,13 @@ protected static function booted()
});
}

public function getSlugOptions() : SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}

public function user(): BelongsTo
{
return $this->belongsTo(User::class);
Expand Down Expand Up @@ -75,6 +79,7 @@ public function toSiteSearchArray()

public function toSearchableArray(): array
{
// Some attributes are not searchable as per SearchActions settings
return [
'id' => $this->id,
'name' => $this->name,
Expand All @@ -84,6 +89,7 @@ public function toSearchableArray(): array
'garnish' => $this->garnish,
'image_url' => $this->getImageUrl(),
'short_ingredients' => $this->ingredients->pluck('ingredient.name'),
'user_id' => $this->user_id,
'tags' => $this->tags->pluck('name'),
'date' => $this->updated_at->format('Y-m-d H:i:s')
];
Expand Down
8 changes: 7 additions & 1 deletion app/Models/CocktailIngredient.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

namespace Kami\Cocktail\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class CocktailIngredient extends Model
{
Expand All @@ -22,4 +23,9 @@ public function cocktail(): BelongsTo
{
return $this->belongsTo(Cocktail::class);
}

public function substitutes(): HasMany
{
return $this->hasMany(CocktailIngredientSubstitute::class);
}
}
Loading

0 comments on commit e8431c4

Please sign in to comment.