diff --git a/apps/Admin/Controllers/AdminDeleteController.php b/apps/Admin/Controllers/AdminDeleteController.php
index 17ac960..4458d61 100644
--- a/apps/Admin/Controllers/AdminDeleteController.php
+++ b/apps/Admin/Controllers/AdminDeleteController.php
@@ -50,6 +50,12 @@ class AdminDeleteController extends Abstract\AdminBaseController
'field_display' => 'ingredient_name',
'back_to' => 'admin:ing-cat-rel',
],
+ [
+ 'url_name' => 'recipe-reviews',
+ 'model' => 'Lycoreco\Apps\Recipes\Models\ReviewsModel',
+ 'field_display' => 'field_title',
+ 'back_to' => 'admin:review',
+ ],
];
protected $template_name = APPS_PATH . '/Admin/Templates/delete.php';
diff --git a/apps/Admin/Controllers/AdminReviewControler.php b/apps/Admin/Controllers/AdminReviewControler.php
new file mode 100644
index 0000000..debce04
--- /dev/null
+++ b/apps/Admin/Controllers/AdminReviewControler.php
@@ -0,0 +1,44 @@
+ 'title',
+ 'input_type' => 'text',
+ 'input_attrs' => ['required']
+ ],
+ [
+ 'model_field' => 'rating',
+ 'input_type' => 'number',
+ 'input_attrs' => ['required']
+ ],
+ [
+ 'model_field' => 'content',
+ 'input_type' => 'textarea',
+ 'input_attrs' => ['required']
+ ],
+ [
+ 'model_field' => 'status',
+ 'input_type' => 'select',
+ 'input_attrs' => ['required'],
+ 'input_values' => ReviewsModel::STATUS
+ ],
+ [
+ 'model_field' => 'created_at',
+ 'input_type' => 'text',
+ 'dynamic_save' => false,
+ 'input_label' => 'Created at',
+ 'input_attrs' => ['disabled']
+ ]
+ );
+}
\ No newline at end of file
diff --git a/apps/Admin/Controllers/AdminReviewListController.php b/apps/Admin/Controllers/AdminReviewListController.php
new file mode 100644
index 0000000..c531ed5
--- /dev/null
+++ b/apps/Admin/Controllers/AdminReviewListController.php
@@ -0,0 +1,17 @@
+ 'field_title',
+ 'Rating' => 'field_rating',
+ 'Status ' => 'get_status()',
+ 'Creaated at' => 'field_created_at',
+ );
+ protected $single_router_name = 'admin:review';
+ protected $verbose_name = "review";
+ protected $verbose_name_multiply = "reviews";
+}
\ No newline at end of file
diff --git a/apps/Admin/Templates/components/admin-header.php b/apps/Admin/Templates/components/admin-header.php
index de94a83..8ac60d3 100644
--- a/apps/Admin/Templates/components/admin-header.php
+++ b/apps/Admin/Templates/components/admin-header.php
@@ -26,6 +26,11 @@ $sidebar_links = [
'name' => 'Ingredients',
'icon' => 'fa-solid fa-carrot',
'router_name' => 'admin:ingredient-list'
+ ],
+ [
+ 'name' => 'Reviews',
+ 'icon' => 'fa-solid fa-comment',
+ 'router_name' => 'admin:review-list'
]
];
?>
diff --git a/apps/Admin/urls.php b/apps/Admin/urls.php
index 22e813f..ddc6efa 100644
--- a/apps/Admin/urls.php
+++ b/apps/Admin/urls.php
@@ -12,6 +12,7 @@ $admin_urls = [
new Path('/admin/recipes',new Controllers\AdminRecipeListController(), 'recipe-list'),
new Path('/admin/categories',new Controllers\AdminCategoryListController(), 'category-list'),
new Path('/admin/ingredients',new Controllers\IngredientListController(), 'ingredient-list'),
+ new Path('/admin/reviews', new Controllers\AdminReviewListController(), 'review-list'),
////// Single object ///////
// User
@@ -35,6 +36,9 @@ $admin_urls = [
new Path('/admin/ingredient/[:int]', new Controllers\IngredientController(), 'ingredient'),
new Path('/admin/ingredient/new', new Controllers\IngredientController(true), 'ingredient-new'),
+ // Reviews
+ new Path('/admin/review/[:int]', new Controllers\AdminReviewControler(), 'review'),
+
// Recipe ingedient relation
new Path('/admin/recipe/[:int]/ingredients', new Controllers\IngredientRecipeRelListController(), 'ing-cat-rel-list'),
new Path('/admin/recipe/ingredient/[:int]', new Controllers\IngredientRecipeRelController(), 'ing-cat-rel'),
diff --git a/apps/Index/Controllers/HomepageController.php b/apps/Index/Controllers/HomepageController.php
index eb9111d..fbe7d17 100644
--- a/apps/Index/Controllers/HomepageController.php
+++ b/apps/Index/Controllers/HomepageController.php
@@ -5,6 +5,7 @@ namespace Lycoreco\Apps\Index\Controllers;
use Lycoreco\Apps\Recipes\Models\CategoryModel;
use Lycoreco\Apps\Recipes\Models\RecipeModel;
use Lycoreco\Apps\Recipes\Models\RecipeUserMenu;
+use Lycoreco\Apps\Recipes\Models\ReviewsModel;
use Lycoreco\Includes\BaseController;
require_once(INCLUDES_PATH . '/Const/recipes.php');
@@ -29,6 +30,16 @@ class HomepageController extends BaseController
$dayNumber = date("w");
$dayofweek = DAYS_OF_WEEK[$dayNumber];
+ $context['reviews'] = ReviewsModel::filter(array(
+ [
+ 'name' => 'obj.status',
+ 'type' => '=',
+ 'value' => 'publish'
+ ]),
+ ['-obj.created_at'],
+ 6
+ );
+
if(CURRENT_USER) {
$context['usermenu_recipe_prefetch'] = RecipeUserMenu::get_prefetch_recipes(CURRENT_USER, $dayofweek);
}
diff --git a/apps/Index/Templates/index.php b/apps/Index/Templates/index.php
index 9c03da4..c4e3197 100644
--- a/apps/Index/Templates/index.php
+++ b/apps/Index/Templates/index.php
@@ -86,104 +86,23 @@ the_header(
Recent User Reviews
-
+
+
-

+
-
Just Like Mom's
+
= $review->recipe_title ?>
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
+
= $review->get_excerpt() ?>
-
-
-
-
-

-
-
Just Like Mom's
-
-
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
-
-
-
-
-
-

-
-
Just Like Mom's
-
-
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
-
-
-
-
-
-

-
-
Just Like Mom's
-
-
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
-
-
-
-
-
-

-
-
Just Like Mom's
-
-
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
-
-
-
-
-
-

-
-
Just Like Mom's
-
-
-
I made this spaghetti last night and it was absolutely delicious! The sauce was rich and
- flavorful—just the right balance of garlic, herbs, and tomato. I added a pinch of chili
- flakes for a little kick, and it turned out perfect...
-
-
+
diff --git a/apps/Recipes/Controllers/SingleRecipeController.php b/apps/Recipes/Controllers/SingleRecipeController.php
index e50a99e..0ba4ebe 100644
--- a/apps/Recipes/Controllers/SingleRecipeController.php
+++ b/apps/Recipes/Controllers/SingleRecipeController.php
@@ -2,16 +2,51 @@
namespace Lycoreco\Apps\Recipes\Controllers;
+use Exception;
use Lycoreco\Apps\Recipes\Models\IngredientInRecipeModel;
use Lycoreco\Apps\Recipes\Models\RecipeModel;
+use Lycoreco\Apps\Recipes\Models\ReviewsModel;
use Lycoreco\Apps\Users\Models\UserModel;
use Lycoreco\Includes\BaseController;
+use Lycoreco\Includes\Model\ValidationError;
use Lycoreco\Includes\Routing\HttpExceptions;
class SingleRecipeController extends BaseController
{
protected $template_name = APPS_PATH . '/Recipes/Templates/single.php';
+ protected function post()
+ {
+ if(!CURRENT_USER) {
+ $this->context['reviews_error'] = 'You are not authorized';
+ return;
+ }
+ $rating = $_POST['rating-select'] ?? null;
+ $title = $_POST['review-title'] ?? null;
+ $content = $_POST['review-body-input'] ?? null;
+
+ $recipe = $this->get_model();
+
+ $review = new ReviewsModel();
+ $review->field_title = $title;
+ $review->field_content = $content;
+ $review->field_rating = $rating;
+ $review->field_status = 'pending';
+ $review->field_user_id = CURRENT_USER->get_id();
+ $review->field_recipe_id = $recipe->get_id();
+
+ try {
+ $review->save();
+ $this->context['display_review_form'] = false;
+ }
+ catch(ValidationError $ex) {
+ $this->context['reviews_error'] = $ex->getMessage();
+ }
+ catch(Exception $ex) {
+ $this->context['reviews_error'] = 'Unexpected error';
+ }
+ }
+
protected function get_model()
{
if (isset($this->__model))
@@ -39,6 +74,7 @@ class SingleRecipeController extends BaseController
$context['recipe'] = $recipe;
+ $context['display_review_form'] = true;
$context['author'] = UserModel::get(array(
[
@@ -56,6 +92,42 @@ class SingleRecipeController extends BaseController
]
));
+ $context['reviews'] = ReviewsModel::filter(array(
+ [
+ 'name' => 'obj.recipe_id',
+ 'type' => '=',
+ 'value' => $recipe->get_id()
+ ],
+ [
+ 'name' => 'obj.status',
+ 'type' => '=',
+ 'value' => 'publish'
+ ],
+ ));
+ $context['reviews_average'] = '0.0';
+ if(!empty($context['reviews'])) {
+ $sum = array_sum(array_column($context['reviews'], 'field_rating'));
+ $count = count($context['reviews']);
+ $context['reviews_average'] = number_format($sum / $count, 1);
+ }
+
+ if(CURRENT_USER) {
+ $review_exists = ReviewsModel::count(array(
+ [
+ 'name' => 'obj.recipe_id',
+ 'type' => '=',
+ 'value' => $recipe->get_id()
+ ],
+ [
+ 'name' => 'obj.user_id',
+ 'type' => '=',
+ 'value' => CURRENT_USER->get_id()
+ ]
+ ));
+ if($review_exists > 0)
+ $context['display_review_form'] = false;
+ }
+
return $context;
}
}
diff --git a/apps/Recipes/Models/ReviewsModel.php b/apps/Recipes/Models/ReviewsModel.php
new file mode 100644
index 0000000..2ef4c69
--- /dev/null
+++ b/apps/Recipes/Models/ReviewsModel.php
@@ -0,0 +1,121 @@
+ 'int',
+ 'title' => 'string',
+ 'content' => 'string',
+ 'rating' => 'int',
+ 'status' => 'string',
+
+ 'recipe_id' => 'int',
+ 'user_id' => 'int',
+ 'created_at' => 'DateTime',
+ ];
+ static protected $additional_fields = array(
+ [
+ 'field' => [
+ 'us.username AS author_username'
+ ],
+ 'join_table' => 'users us ON us.id = obj.user_id'
+ ],
+ [
+ 'field' => [
+ 're.title AS recipe_title',
+ 're.image_url AS recipe_image'
+ ],
+ 'join_table' => 'recipes re ON re.id = obj.recipe_id'
+ ]
+ );
+
+ public static function init_table()
+ {
+ $result = db_query('CREATE TABLE ' . static::$table_name . ' (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+
+ title VARCHAR(255) NOT NULL,
+ content TEXT NOT NULL,
+ rating INT NOT NULL,
+ status VARCHAR(20) NOT NULL CHECK (status IN (\'publish\', \'pending\')),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ recipe_id INT NOT NULL,
+ user_id INT NOT NULL,
+
+ FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE,
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
+ );');
+ return $result;
+ }
+
+ public function get_html_content(): string
+ {
+ return nl2br(trim($this->field_content));
+ }
+ public function get_status()
+ {
+ return ucfirst($this->field_status);
+ }
+ public function get_date()
+ {
+ $date = new DateTime($this->field_created_at);
+ return $date->format('d.m.Y');
+ }
+ public function get_excerpt()
+ {
+ $max_length = 100;
+ $excerpt = mb_substr($this->field_content, 0, $max_length);
+
+ if(mb_strlen($this->field_content) > $max_length)
+ $excerpt .= '...';
+
+ return $excerpt;
+ }
+ public function get_recipe_image()
+ {
+ $recipe = new RecipeModel();
+ $recipe->field_image_url = $this->recipe_image;
+ return $recipe->get_image_url();
+ }
+ public function valid() {
+ $errors = array();
+
+ if(empty($this->field_title))
+ $errors[] = 'Title field is empty';
+
+ if(empty($this->field_content))
+ $errors[] = 'Content field is empty';
+
+ $this->field_rating = (int)$this->field_rating;
+ if($this->field_rating < 1 || $this->field_rating > 5) {
+ $errors[] = 'Rating must be between 1 and 5';
+ }
+
+ if (empty($errors))
+ return true;
+
+ return $errors;
+ }
+}
\ No newline at end of file
diff --git a/apps/Recipes/Templates/single.php b/apps/Recipes/Templates/single.php
index 90395eb..1798525 100644
--- a/apps/Recipes/Templates/single.php
+++ b/apps/Recipes/Templates/single.php
@@ -130,45 +130,40 @@ the_header(
Reviews
-
Average Rating: 4.5
+
Average Rating: = $context['reviews_average'] ?>
All Reviews
+
+
Nothing to show
+
+
+
-
5 Divine disc of
- deliciousness!
+
= $review->field_rating ?> = $review->field_title ?>
- Blessings of Aqua-sama upon you, fellow foodies! As a proud and very sane member of the Axis
- Cult, I recently partook in a holy culinary experience that rivaled even the sacred waters
- of the Blue Lake: a homemade pepperoni and mushroom pizza!
- The moment I laid eyes on that glorious golden crust, I knew—Aqua-sama herself must have
- guided my ovens temperature dial! The cheese was melted with such heavenly grace,
- stretching with every bite like the ribbons of our goddesss divine garments. The pepperoni?
- Spicy circles of salvation! And the mushrooms—earthy little blessings sent straight from the
- soil Aqua purifies daily!
- The crust had the perfect balance of crispy edge and fluffy soul. It crackled like the
- laughter of a cultist pranking a stubborn Eris follower (teehee~). Each bite sang praises in
- my mouth: “Axis! Axis! AXIS!!”
- Would I recommend this pizza? Of course! Id even offer it as tribute at the next festival
- in Aquas honor. Try it yourself and bask in the savory enlightenment. And remember: if it
- tastes weird, just pour holy water on it. Or beer. Aqua would approve either way.
- Rating: 5 out of 5 Axis Blessings!(Any lower would be heresy.)
+ = $review->get_html_content() ?>
+
+