Discussion continued from
Hi, all.
I am banging my head against a brick wall with this one. I have previously posted on the issue but the thread has died and I have some new code/attempts to post anyway. I am up against a deadline with this one (probably shouldn’t have put too much faith in my ability to understand the framework deeply enough: should have built the new site without a framework and left using it until I’d had more time to fully understand it. But… I didn’t and now it’s going to take me longer to build without it than to struggle on and understand it. Retrospect - a wonderful thing!)
I am attempting to use the framework built throughout this book in my first “other” website. I am basically attempting to build a 3-level nav bar. Forget the layout (I can sort that myself), all I need is the data. Here goes:
Imagine I run a restaurant:
I employ 3 chefs: chefA, chefB, chefC.
I offer the usual 3 courses: starter, main, dessert.
I have customers who want to use the website to choose 3 courses by the SAME chef. (Yeah, odd in this context; not in the actual site I’m designing. Bear with me… :))
So, database tables:
“chefs”
chefId, chefName
1, chefA
2, chefB
3, chefC
“meals”
mealId, mealName
1, Avacado Toast
2, Prawn Cocktail
3, Sirloin Steak
4, Sea Bass
5, Banoffie Pie
6, Ice Cream
“chefMeal”
chefId, mealId
1, 1
2, 1
2, 2
3, 2
1, 3
1, 4
2, 3
3, 4
etc
“courses”
id, name
1 Stater
2 Main
3 Dessert
Easy enough so far. Here’s what I need:
I need to adjust Tom’s Joke site so that my site has 3 navigation links in the navbar: Chef A, Chef B, Chef C
When the visitor clicks on a chef they get a submenus which looks something like this: (all shown for clarity but, obviously, they’d only see one):
Chef A Chef B Chef C
Starters Starters Starters
Avacado Toast Avacado Toast Prawn Cocktail
Prawn Cocktail
Mains Mains Mains
Sirloin Steak Sirloin Steak Sea Bass
Sea Bass
Desserts
etc…
That’s it.
Tom, the author of the book has very kindly offered the following which I have attempted to integrate into my website:
- Set up entity classes for the various tables the same way the Joke class is set up in the book. Your Chef class would like this:
class Chef {
private $chefMealTable;
private $mealsTable;
private $cachedMeals
public $id;
public $chefId;
public function __construct(\Ninja\DatabaseTable $chefMealTable, \Ninja\DatabaseTable $mealsTable) {
$this->chefMealTable = $chefMealTable;
$this->mealsTable = $mealsTable;
}
public function getMeals($courseId) {
if (empty($this->cachedMeals)) {
$this->cachedMeals = $this->chefMealTable->find('chefId', $this->chefId);
}
$meals = [];
foreach ($this->cachedMeals as $chefMeal) {
$meal = $this->mealsTable->findById($chefMeal->id);
if ($meal->courseId == $course->id) {
$meals[] = $meal;
}
}
return $meals;
}
}
- Then you should be able to do this:
$chefs = $chefsTable->findAll();
$courses = $courses->findAll(); // not sure if Tom meant $coursesTable here.
foreach ($chefs as $chef) {
foreach ($courses as $course) {
echo $course->name;
//get the corresponding records from the chefMeal table like the getAuthor method in the `Joke` class
foreach ($chef->getMeals($course->id) as $meal) {
echo $meal->mealName;
}
}
}
Looks like it makes perfect sense to me. Here’s what I’ve done so far to integrate this:
-
I’ve created a “Chef” entity class, just like Tom’s above. I didn’t create any others (for the other tables) as I can’t see where, in Tom’s code, they are used.
-
I modified the code in EntryPoint->run() which sends the layout.html.php template through the loadTemplate method like so. (I did this as this is where the navigation is. So, I’m basically creating the variables to pass into the template.)
$nav = $this->routes->getNav();
echo $this->loadTemplate('layout.html.php', [
'loggedIn' => $authentication->isLoggedIn(),
'output' => $output,
'title' => $title,
'variables' =>
[
'chefs' => $nav[chefs],
'courses' => $nav[courses]
]
]);
… and here’s how I’ve modified the template to use the variables …
<ul>
<li><a href="/">Home</a></li>
<?php foreach ($chefs as $chef): ?>
<li><?= $chef->name; ?>
<?php foreach ($courses as $course): ?>
<li><?= $course->name; ?>
<?php foreach ($chef->getMeals($course->id) as $meal): ?>
<li><?= $meal->mealName; ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php endforeach; ?>
</ul>
- getNav() needs two instances of DatabaseTable (chefsTable and coursesTable), so I added the following code to IjdbRoutes:
a) Private Variables:
private $chefMealTable; // needed for the arguments of chefsTable, below
private $mealsTable; // ditto
private $chefsTable;
private $coursesTable;
b) Constructor…
$this->chefMealTable = new \Framework\DatabaseTable($pdo, 'chefMeal', 'chefId');
$this->mealsTable = new \Framework\DatabaseTable($pdo, 'meal', 'mealId');
$this->chefsTable = new \Framework\DatabaseTable($pdo, 'chef', 'chefId', '\Its\Entity\Chef', [&$this->chefMealTable, &$this->mealsTable]);
$this->coursesTable = new \Framework\DatabaseTable($pdo, 'course', 'id');
c) new method
public function getNav() {
$chefs = $this->chefsTable->findAll();
$courses = $this->coursesTable->findAll();
return $nav = ['chefs' => $chefs, 'courses' => $courses];
}
(I also added a line into \Framework\Routes for the new, getNav(), method, as it is actually the routing table (API) not IjdbRoutes that is passed into EntryPoint as an argument.
That’s it.
The error I’m getting is this:
“SQLSTATE[HY000]: General error: could not call class constructor in /var/www/www.its-ltd.local/classes/Framework/DatabaseTable.php:132”
Line 132 is the final line in the findAll() method, which reads:
return $result->fetchAll(\PDO::FETCH_CLASS, $this->className, $this->constructorArgs);
In the “process” (as shown by xDebug within NetBeans), this happens - as you’ve guessed - when EntryPoint->run() calls getNav() when loading the layout template.
The “class constructor” that it “could not call” is the Chef entity class.
I have tried and tried to work this out. It’s not a particularly useful error message.
Any help would be greatly appreciated.
If you think you can help but haven’t read the book, I am happy to post code that is “missing” from your understanding of the problem, just let me know.
Thanks.
Mike