Is this right way to do:
I create a model, controller, view for localhost/users and do the same for localhost/hello-world. Now that I have two views (template designs) for controllers how can I use them in third controller like localhost/home (DashboardController in the code)?
namespace app\controllers;
use app\models\Users;
use app\controllers\HelloWorldController;
class DashboardController extends \lithium\action\Controller {
public function index() {
$users = Users::find('first');
$hello = HelloWorldController::to_string();
return compact('users', 'hello');
}
}
Do I have to style again $users and $hello in DashboardController view and in other new controllers where I want to use multiple models, or I can use their own views which I made at the beginning? This question is really bothering me, becouse I'm new in MVC and frameworks.
If you only require sections you could use Lithium elements.
echo $this->view()->render(array('element' => 'name of element'), array('datavar' => $passingDataIn))
If you need to just render the same view again you could essentially tell the Controller method to use the view:
return $this->render(array('template' => 'dashboard/index.html.php')));
Related
I am using Laravel 5. if I have written a function on Model class and takes it's object by laravel eloquent to any view file like cars.blade.php file and now if I call any model function from cars.blade.php
Like Model Class
Car.php
public function totalModels() {
return App\Models\CarModel::where('id',$this->modelId)->count();
}
Cars.blade.php
<span>Available Models : {{ $car->totalModels() }}</span>
So My Questions are
When this function will call ?
Is this function slow the page ?
Is this a best practice to do it ?
if there is any foreach loop then how will this function will behave for each object ?
Thanks
The method you're searching for is withCount:
Create a relation to App\Models\CarModel in the car model and eager load it with withCount to prevent sending too many queries in the loop and slowing down the page too much:
Car.php
namespace App\Models;
class Car
{
// ...
public function models()
{
return $this->hasMany('App\Models\CarModel', 'id', 'modelId');
}
// ...
}
CarsController.php
namespace App\Http\Controllers;
class CarsController extends Controller
{
// ...
public index()
{
$cars = App\Models\Car::withCount('models')->get();
return view('cars', compact('cars'));
}
// ...
}
cars.blade.php
#foreach ($cars as $car)
{{-- ... --}}
<span>Available Models: {{ $car->models_count }}</span>
{{-- ... --}}
#endforeach
Is this a best practice to do it ?
You are coupling VIEW part of MVC architecture to the Model itself which is not good practice.
When this function will call?:
This will run as soon as laravel templating engine will render that blade.
What will happen:
It will make an extra call to the database get all the rows and then perform a collection count() which is much slower than mysql count().
Is this function slow the page ? Yes. Improve the query performance a bit at least:
App\Models\CarModel::select(DB::raw('count(*) as total_cars'))
->where('id',$this->modelId)
->pluck('total_cars');
This query is similar to what happens with withCount() method via a relationship.
If there is any foreach loop then how will this function will behave for each object ?
If you are foreaching an object and doing a call to get the count then foreach itteration an extra call to the database will happen similar to N+1 problem
A better way of doing it, check #Dan answer.
It is possible to extend or use different class during run time?
Example:
Let say we have a model called Player (Our A -model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Player extends Model{
}
And we have 2 other models (B and C models)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
protected $connection= 'db_b';
class PlayerInfoB extends Model{
function getName(){
return $this->name;
}
}
Our C model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
protected $connection= 'db_c';
class PlayerInfoC extends Model{
function getName(){
return $this->name_g;
}
}
How Model A (Player) can extend Model B or C during run time based on configuration or other data
Why I need this.
I have a 2 or more different tables, this tables columns have different names, so for example:
Table 1 - name
Table 2 - name_g
Table 3 - name_full
So I need a wrapper that I can always call getName(), without checking what table is used now.
$player = Player::get();
echo $player->getName();
If something is not clear, please comment and I will update my question.
Update based on madalin-ivascu answer can be done this way?
class Player extends Model{
protected $model;
public function __construct(){
$this->setModel();
parent::__construct();
}
protected function setModel(){
$this->model = $this->column_model_name
}
function getAttributeName(){
return $this->model->getName();
}
}
It is not possible to compose a calss at runtime without using eval or or dirty hacks. You have to reconsider your class design, because it's very unlikely that you need to do that with a good design.
What you can do is changing table and db connection at runtime on the model instance using methods setTable and on:
Player::on('db_b')->setTable('player_info_b')->find($id);
Another approach (preferable) would be defining the model PlayerInfoC and PlayerInfoB that extend your Player model, and then based on your condition you instantiate the class B or C when needed.
In your code your script must have a state that you check in order to know when to use the correct model?
In this case why not use a parameter in get name?
class Player extends Model{
function getName($field){
return isset($this->{$field}) ? $this->{$field} : null;
}
}
If you do this a lot then use magic methods:
class Player extends Model{
function __get($key){
return isset($this->{$field}) ? $this->{$field} : null;
}
}
...
echo $myModelInstance->{$field};
http://php.net/manual/en/language.oop5.overloading.php#object.get
In Laravel when you have pulled data back via a collection method it does this magic method anyway since all attributes are stored in a nested object called attributes so the __set() and __get() looks something like this:
function __get($key){
return isset($this->attributes->{$key}) ? $this->attributes->{$key} : null;
}
function __set($key, $value){
return $this->attributes->{$key} = $value;
}
The latter with attributes sub set is advised, this way you prevent data conflicts with database column names returned vs names you have already used in a model.
This way you only have to manage one attribute name as a reserved name in every model you create and not worry amount the hundreds of var names you use overwriting another in a model or extension of a model.
use that model value to call the function
$player = Player::get();
echo Player::getName($player->id,'PlayerInfoC');
in the Player model you simply call
public static function getName($id,$class)
return $class::where('player_id',$id)->getName();//each class will have the function
}
ps: you will need to do some validation to test if the class exist with that name
another option will be to create relationships between the models
I have a dynamic property user in my model:
class Training extends Model
{
...
public function user()
{
return $this->belongsTo('App\User');
}
}
And I can easy get username in controller like this:
Training::find(1)->user->name
But I don't know how to perform the same in view. I tried this:
Controller:
return view('training/single', Training::find(1));
View:
{{ $user->name }};
but without success, I'm getting error Undefined variable: user. So it's look like I can't access dynamic property in view.
Any idea how can I use dynamic property in views?
I fear that's not really possible. There's no way to set the $this context in your view to the model. You could convert the model into an array with toArray() but that would include the related model and you would have to access it with $user['name'].
I personally would just declare the user variable explicitly:
$training = Training::find(1);
return view('training/single', ['training' => $training, 'user' => $training->user]);
Use eager loading
return view('training/single', Training::with('user')->find(1));
My ultimate goal is to print the contents of a table in a database to a table in HTML using Laravel's ORM.
Beyond that I know how to configure the database file, but is there any other things i need to configure.
From my understanding I need three files. Do i need to make a controller? If so how does that work?
Item Class
Route.php
views.php
Here is what I have so far
Item.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Item extends Model {
protected $table = 'items';
protected $primaryKey = 'item_id';
public function products()
{
return $this->belongsTo('item_id', 'item_name', 'item_cost');
}
}
routes.php
<?php
Route::get('Product', function()
{
$products = \App\items::all();
return view('Items', $items);
});
and I have no idea how to create a view in HTML.
I know I'm completely off at this point, but I've read the documentation and I am still completely lost when they reference things like URI and have URL in the same sentence without defining or even linking to it elsewhere in the documentation.
Actually, you have declared your Eloquent ORM like this:
class Item extends Model {
// ...
}
But you are using that model/class like this:
$products = \App\items::all();
In this case, \App\items doesn't exist, it should be:
$items = \App\Item::all(); // <-- Item not items
return view('items', $items); // <-- $items not $products
You asked: Do i need to make a controller?
In this case, no, it'll work fine with the anonymous function but using a Controller is better for many reasons.
Update:
Create a view in resources/views as items.blade.php and you can print out the items through a foreach loop. Check the documentation.
How can customised my query.. this is my current code in my controller:
class PostController extends AbstractActionController
{
private $userTable;
// CRUD
// retrieve
public function indexAction(){
return new ViewModel(
array(
'rowset' => $this->getPostsTable()->select(),
)
);
}
public function getPostsTable(){
if(!$this->userTable){
$this->userTable = new TableGateway(
'posts',
$this->getServiceLocator()->get('Zend\Db\Adapter\Adapter')
);
}
return $this->userTable;
}
}
How can i order the result to descending?
And how to join another table with that code?
First of all, Zend framework is an MVC Framework.
Means that your Data Object Access MUST be in Model layer NOT IN Controller.
Your PostController can't have Model logic in it, it's bad. And it may be throw so much error that you will not understand directly.
Plus, call getServiceLocator in Controller is a bad practise and it will be removes in Zf3. That's why using Model Layer is recommanded.
For your problem, you have to make a query builder like this :
$sql = new \Zend\Db\Sql\Sql($this->getAdapter());
$select = $sql->select()
->from('tableName')
->columns(array())
->join('tableName2', 'Your ON Clause')
->where(array('if you Have WhereClause'))
->order('Your column DESC');
I use Doctrine but i'm pretty sure (community will confirm this or not) this example may work.