Illuminate - HasMany - Iteration - php

I am just doing my first exercises with Illuminate (Laravel) on my currently handmade database and website. I would like to improve by using MVC and found Illuminate interesting to use for the interaction with my database.
I worked the day on this bloody tiny code and can't find the problem and I hope someone out there has a good idea for me. Many thanks!
Basic question: Why can't I iterate over the courses to the given semester? While it is possible to call a specific course.
use Illuminate\Database\Eloquent\Model as Eloquent;
class Semester extends Eloquent {
protected $table = "tblSemester";
protected $primaryKey = "SemesterID";
public function getCourses() {
// test if semester is correct
echo $this->SemesterID, " - ", $this->Semestertext, "<br>";
// This works fine and returns the expected result
$course = $this->hasMany('Course', 'Semester')->first();
echo $course->Number, " - ", $course->Title;
// This doesn't work. It returns nothing.
// There seems to be no data in $courses (just metadata)
$courses = $this->hasMany('Course', 'Semester');
foreach ($courses as $course) {
echo $course->Number, " - ", $course->Title;
echo "<br>";
}
return "<h1>" . "Test ends" . "</h1>";
}
}
Many thanks!
Tim

First of all
Since you said you are new to 'Laravel' and Want to follow MVC, I thought I may give you some advices.
In laravel Models, don't include database manipulation codes. Instead write them in controllers.
In Models, only include functions which belongs to a single models instance (non static functions).
relationships
query scopes
accessors / modifiers
Example Model
class Semester extends Model
{
protected $table = 'semesters';
// relationships
public function cources()
{
return $this->hasMany(Cource::class);
}
}
Example Controller
class SemesterController extends Controller
{
public function iterateOverCourcesOfGivenSemester()
{
// take first semester as an example.
$givenSemester = Semester::first();
// all the cources belongs to a given semester.
$cources = $givenSemester->cources;
foreach($cources as $cource) {
// Warning! don't echo anything in a controller, Instead return them to a view.
echo $course->Number, " - ", $course->Title;
echo "<br>";
}
}
}

Why is this not working:
public function cources()
{
return $this->hasMany(Cource::class);
}
But this does:
public function cources()
{
return $this->hasMany('Course', 'Semester')->get();
}

Related

Laravel - custom caching property (getter?)

I just can't understand how to do in "the laravel way" what the example below describes without messing with model properties from the database.
To be clear, I'm looking for some core Laravel magic so I don't need to do the $memoryCache thing manually
Imagine the getProp() method is a database call that I don't want to run more than once, ie keeping the result in memory until PHP is done running.
Example class:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Test extends Model
{
public $memoryCache = [];
public function propA() {
return $this->getProp() . '_A';
}
public function propB() {
return $this->getProp() . '_B';
}
private function getProp() {
if (!array_key_exists('prop', $this->memoryCache)) {
echo "Do this only once\n";
$this->memoryCache['prop'] = 'DBData_' . rand(1,5);
}
return $this->memoryCache['prop'];
}
}
Running it:
$test = new \App\Models\Test;
echo "{$test->propA()}\n";
echo "{$test->propB()}\n";
Output:
Do this only once
DBData_5_A
DBData_5_B
Bonus!
Would be even better if I could somehow use PHP getters/setters so that I could just call them like class "properties" like this:
$test = new \App\Models\Test;
echo "{$test->propA}\n";
echo "{$test->propB}\n";
There really isn't anything built in anywhere that you could use, what you are doing is fine; this is the same type of check that Eloquent does when you use the dynamic property for a relationship. It checks if it has been loaded and if not loads it then returns the loaded relationship (that is saved on the instance, cached in the same type of way).
The "Bonus" part:
If you want to access those as properties instead of methods you can do that with an "accessor":
public function getPropAAttribute()
{
return $this->getProp() . '_A';
}
Now you can access the propA property of the model:
$model->propA;
Laravel 8.X Docs - Eloquent - Accessors and Mutators - Defining an Accessor

Get eloquent model from DB including sub models

I'm learning PHP and the Laravel framework and i'm getting stuck on loading some data from the database.
I have 2 models, Game and Player
Player model (curently empty):
class Player extends Model
{
}
Game model:
class Game extends Model
{
public $Players = [];
public function __construct(array $players = [])
{
$this->Players = $players;
}
public function SaveGame()
{
if($this->save()) {
Log::info("New game created with id " . $this->id);
}
foreach ($this->Players as $key => $player){
if ($this->Players()->save($player)){
Log::info("player added to game with id " . $player->id);
}
}
}
static function GetGame($gameId)
{
Return Game::find($gameId);
}
public function Players()
{
return $this->hasMany(Player::class );
}
}
And i have a GameController:
class GameController extends Controller
{
public function create()
{
$players = [];
for ($i = 1; $i <= 3; $i++) {
$player = new Player();
array_push($players,$player);
}
$game = new Game($players);
$game->SaveGame();
return $game;
}
public function LoadGame($id)
{
$game = Game::GetGame($id);
return $game;
}
}
If i call the create() method of my GameController, it creates a game in my Game table and 3 players in the player table with the gameID of the corresponding game so i think the relation works fine.
But if i check the response of the create() method i get only this:
{
"updated_at": "2018-03-01 15:13:37",
"created_at": "2018-03-01 15:13:37",
"id": 3952
}
Also the LoadGame() function returns just the game. Actually i need the game with all its players inside. Something like this:
{
"updated_at": "2018-03-01 15:13:37",
"created_at": "2018-03-01 15:13:37",
"id": 3952,
"players":[
{"name":"player1"},
{"name":"player2"},
{"name":"player3"},
],
}
What i'am doing wrong?/How do i get the wished result?
I'm also not sure if the Models are the right place to do the database actions and the controllers are the place to create the objects like i do in the posted code. If my "code structure" is not the best practice, some advice in this is also welcome!
To return the relationship you can use:
protected $appends = ['Players']
But to be honest, I think you may want to re-review the Laravel relationships docs to see how to do some of these things properly. Doing a bit of rewriting would make your life a bit easier in the future.
A couple notes for you:
Remove the GetGame method as it's arbitrary. By the time you've already pulled in the class App\Game you will be able to run the find method on it. You can just go: App\Game::find($id)
Remove the public $Players, as well as the construct, as these are unnecessary and breaks the relationship. Laravel makes it so that when you've created a relationship you can access it as if it's a variable within that class. Example: $game->players. You can kind of ignore the fact that the relationship is created as a method.
After doing those two things above, you can modify the protected $appends; variable and view your relationship when displaying the model.
Its so easy!, i found the solution by simply experimenting arround as i could not find a solution on the internet.
In the Game model I removed the
constructor (create own constructor in Laravel seems to be not recommended)
SaveGame() (save my game in the controller now)
GetGame() (useless, can use Game::find(x) in controller)
In the GameController i changed my create() function a bit. This is the result:
public function create()
{
$game = new Game(); // creates a new game
$game->Save(); // saves the new game into the db
for ($i = 1; $i <= 3; $i++) {
$player = new Player(); // creates a new player
$game->Players()->Save($player); // stores the player to the game in the db
}
}
I also changed my LoadGame() method in my GameController to
public function LoadGame($id)
{
$game = Game::find($id); // Instead of doing this in a own method in my model
$game->players // adds the players to the game object for some reason
return $game; // returns the game (as json) incl. all its players
}
This results in a json string what contains the game vars including a player array with its players. I dont understand why this is working but it is.
The weird thing is if i exchange $game->players for $game->Players() or $game->Players (like the has many method in the model) it does not work. Maybe someone can explain me why this is? (as i cant find any clarifying documentation to this)

Proper way of re-using and passing multiple queries in yii2

My website has Object as the model and ObjectSearch as the searchModel. I realized that I am already having a lot of duplicates in my two different controllers. For example, FirstController has this:
$arr = Yii::$app->db->createCommand('SELECT id, name, address
FROM hosts)->queryAll();
Then at some part of my website SecondController also needs to use that same query.
How can I re-use that query over and over again from different controllers?
I am talking about this:
public function search($params)
{
$query = Host::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]
]);
.
.
.
return $dataProvider;
}
I have another question also. I am planning to do different queries. What is the best approach for making another query? Should I use that
public function search($params)
again and maybe add on the parameter like this?
public function search($params, $cond)
{
if ($cond == true) {
$query = *something;
}
else {
$query = *something_else;
}
.
.
.
return $dataProvider;
}
In your root folder, you are having component folder. Inside component folder, create one page like UserInfoComponent.php. This page will be common and can be use anywhere.
This is the Directory Structure
YourProjectFolder
->assets
->commands
->components
->UserInfoComponent.php
.
.
UserInfoComponent.php
<?php
namespace app\components;
use Yii;
use yii\base\Component;
class UserInfoComponent extends Component
{
.
.
public function getUserDetails() {
$arr = Yii::$app->db->createCommand('SELECT id, name, address FROM hosts')->queryAll();
return $arr;
}
.
.
.
}
Now, how to call this function in your controller or view.
Controller or view
<?
$arrValue = Yii::$app->userinfo->getUserDetails();
?>
For common function you can create an helper class
namespace myapp\myhelperdir
class MyQueryHelper
{
public static function mySelectFunction ($param)
{
// your code for function
return $yourResult;
}
then you can refer to these function in every part of your project simply adding
use myapp\myhelperdir\MyQueryHelper
$myResult = MyQueryHelper::mySelectFunction($myParam);

Using Joins in Eloquent

I'm a relative beginner in Laravel 4 and I'm having trouble retrieving data from a joined table as defined in my model. I tried following the directions in Laravel's documentation here and here.
Here's what I did: I have a model (car.php) that successfully retrieves data from my cars table:
class Car extends Eloquent{
public function parts(){
return $this->hasMany('CarParts');
}
}
Here's my CarParts model (car-parts.php):
class CarParts extends Eloquent{
protected $table = 'car_parts';
public function car(){
return $this->hasOne('Car');
}
}
I tried these to get to the data in the joined table:
echo Car::find(1)->parts;
// also tried:
$cars = Car::find(1)->get();
echo $cars->parts;
But I get this error message: Undefined property:Car::$parts
The best way is to use the dynamic property (->parts) so this should work for you:
$car = Car::find(1);
echo $car->parts;
or
$car = Car::find(1);
foreach($car->parts as $part)
{
echo $part->name;
}
The parts() method gives you a query object, which may be used to filter a little more your parts:
$car = Car::find(1);
$parts = $car->parts()->where('model', '=', 'FORD')->get();
foreach($parts as $part)
{
echo $part->name;
}
Also you probably will have to change your CarParts relation to:
$this->belongsTo('Car');
The problem you are having is in your relationship. The inverse of hasMany() is belongsTo() so with that in mind, your models should looking something like this...
class Car extends Eloquent
{
public function parts()
{
return $this->hasMany('CarParts');
}
}
class CarParts extends Eloquent
{
protected $table = 'car_parts';
public function car()
{
return $this->belongsTo('Car');
}
}
In order to find a car's parts, you can do something like this...
$parts = Car::find(1)->parts;
foreach($parts as $part) {
echo $part->name;
}
You should also add the primaryKey properties to your models as well if they are not following the Laravel standards. Add this to CarPart with the appropriate key.
protected $primaryKey = 'car_part_id';
First, your relations should be defined like this:
<?php
class Car extends Eloquent {
public function parts(){
return $this->hasMany('CarPart');
}
}
class CarPart extends Eloquent {
protected $table = 'car_parts';
public function car(){
return $this->belongsTo('Car');
}
}
You should do this:
Car::parts();
Or this:
$cars = Car::find(1);
$carParts = $cars->parts();
Figured it out... I renamed my "CarParts" model to just "Parts". Also renamed the model file to "parts.php" and it worked. (Also fixed the relationship as edvinas.me noticed... but that didn't seem to matter to the result.) The camel-case thing is strange. Can anyone explain this to me? Here's what it looks like now:
class Car extends Eloquent{
public function parts(){
return $this->hasMany('Parts');
}
}
class Parts extends Eloquent{
protected $table = 'car_parts';
public function car(){
return $this->belongsTo('Car');
}
}
Does eloquent just hate camel case?
Update:
I figured out why I was having this problem with the way I was naming my classes. As it turns out, one of my migration classes was already called CarParts. Simple problem, but I decided to post this update since I think it's pretty easy for someone else to make the same mistake.

PHP MVC - Store User Variables in Controller or Model?

What is the best practices as far as storing variables in the Controller or Model?
For instance when the script is executed. it grabs the user id from the session and gets what type of user it is, Super Admin, Admin, Service Rep, Sales Rep. We also check to see what account the user id belongs too, and grab all the setting for that account.
My questions is where do i store these values, in the controller or model?
Thank you in advance.
In PHP, it is a little strange to think about a true MVC model, because your model, view, and controller can access the $_SESSION.
If, for example, you are going to log a user in, your model would do the following:
class Model{
...
static function login($username, $password){
$result = Model::getUser($username, $password);
if(empty($result)){
return false;
}
else
{
$_SESSION['userid'] = $result['id'];
// Assign other information you think you'll need in the session here
}
}
static function loggedIn(){
if(isset($_SESSION['userid']){
return true;
}
else
{
return false;
}
}
static function getAttribute($attr){
return $_SESSION[$attr];
}
...
}
class Controller{
function someFxn(){
$userInfo = Model::getAttribute('someAttr');
}
}
Obviously this code has to be expended upon, but it should display the concepts correctly. I also used static functions in the model, but you can make the model an object.
My questions is where do i store these settings, in the Model, or pass it back to the controller, and the controller will store these settings?
Depending on how you want to do it, you either fetch the settings every time form the database through the model or you can store them in the session. Storing things in the $_SESSION will allow you to have less database calls. In practice, the model manipulates the $_SESSION or the database. If your model is particular to something (you could make your own user model), then you instantiate that object and store your information in private members.
The point of the controller is to take information form the model and then render your page accordingly. Really a MVC dataflow works this way:
Request is made to controller
Controller gets information form model (this is optional, maybe the controller doesn't need anything from the model)
Model returns information to controller
(Happens if you made a request form the previous step)
Controller passes appropriate information to view.
You store them in the model (Grab them from DB), you pull them with the controller (On page load), and you show the result of them in the View (By calling the controller class when needed).
This is the basic theory of MVC...
Good luck!
I will give you a simple example of a car object that can be sold... this example sucks, but you can understand from it how MVC works...
<?
// Data
class Car
{
private $_color;
public function setColor($newC)
{
$this->_color = $newC;
}
public function getColor()
{
return $this->_color;
}
private $_maxSpeed
public function setMaxSpeed($newMS)
{
$this->_maxSpeed = $newMS;
}
public function getMaxSpeed()
{
return $this->maxSpeed;
}
}
// Example
$car = new Car();
$car->setColor($dbInfo['color']);
$car->setMaxSpeed($dbInfo['maxSpeed']);
// Controller
class Sales
{
. . .
public function SaleCar(Costumer $costumer, Car $car, $quantity)
{
if($car->getColor() == "red") // Red is expensive color...
$car->MultiplyPriceBy(1.5); // Just an example...
else
$car->SubsetQuantityBy($quantity); // The car has quantity propery as well... and so on...
$costumer->setPaymentType("Credit-card");
. . .
$costumer->Pay($quantity * $car->getPrice());
return $finalPrice; // $quantity * $car->getPrice()
}
. . .
}
// View
class SalesPanel
{
. . .
public function output()
{
foreach($this->cars as $car)
{
if(in_array($car->getID(), $_POST['car_id']))
Sales->SaleCar(Costumer::GetCostumerFromID($_SESSION['uid']), $car, $_POST['quanityty']);
}
$output = . . .
$output .= "Car model GHi675 old by . . . "; // Get info from controller
}
. . .
}
?>

Categories