A Yuga ViewModel is a special class that represents the data model used in a specific view.
What is a ViewModel?, What makes it different from a controller?
In a yuga application, every view has a code behind filewhich in this case is our view model, View models can work as controllers but the main difference lies in how they couple with views, a controller can return any view and it doesn't interact with the view itself while a view model interacts directly with the view and returns only that view. In fact, you don't tell it the view it has to return, It already knows it.
A view model in a yuga application binds a form to a real database model such that the developer doesn't need to do the mapping of form fields to a model them selves.
It has automatic validation of form fields. This can turn out to be a time saver, if custom validation is needed, a validate method is provided to a model to which the view model is bound and the view model will run that method instead of the default.
While you can do pretty much everything from within a controller, a view model simplifies your work by taking away tasks like validation and form-model binding.
Basic ViewModels
Defining ViewModels
Below is an example of a basic ViewModel class. Note that the ViewModel extends the base ViewModel class that comes with Yuga. The base class provides a few convenience methods such as the onPost, onLoad, onGet methods, which can be used whenever those events occur.
namespaceApp\ViewModels;classUserViewModelextendsApp{/** * Create a new UserViewModel ViewModel instance. * * @returnvoid*/publicfunction__construct(){parent::__construct();}/** * Handle any form data that has been submited*/publicfunctiononPost($model){}/** * Load or / manupulate data when its a get request*/publicfunctiononGet(){}/** * Load or / manupulate data before the page loads and feed it to the page*/publicfunctiononLoad(){}}
What's in the App class that the above class is extending? Let's find out
The route that corresponds to this UserViewModel is as below:
The Yuga service container is used to resolve all Yuga ViewModels. As a result, you are able to type-hint any dependencies your ViewModel may need in its constructor. The declared dependencies will automatically be resolved and injected into the ViewModel instance:
Creating ViewModels using yuga console command
ViewModels can be created using the php yuga make:viewmodel command
i.e php yuga make:viewmodel UserViewModel would produce the following scaffold:
Model binding to the ViewModel
Think of this as an easier way of mapping every form value to an appropriate Object attribute or property. In yuga, this works like magic.
When a form is submitted, the ViewModel looks for the bound Model from the scope and maps every form field to a property on that Model and finally tries to run a validator to every field on the form to make sure that every form field is not empty for starters.
The default bound model is \Yuga\Models\ElegantModel::class But of course the table that is bound to this model is elegant_models which basically doesn't make any sense for every form, so how do we bind a model to a form, Well, there're two ways of doing this,
You may skip binding to the form yourself and instead set a table to be bound to the ElegantModel class, this is done as below:
$this->setTable("my_table"); inside of the view-model's constructor.
Or you can bind any other model you would like to use instead of ElegantModel as below:
Basic Structure
When a form is submitted and it is a post request method, the onPost method is run and so this is where your code for form manipulation should reside.
Example of a view (My.php)
The code behind to the above view is below: (MyViewModel.php)
The route corresponding to the above ViewModel could be any route but Let’s say that it's:
The model parameter in the onPost method in the MyViewModelViewModel, is the bound model to the form inside the My.php html file. It can only be an instance of Yuga\Database\Elegant\Model for it to work well with the ViewModel.
By default, the bound model is Yuga\Models\ElegantModel
Like the code above, you just have to call the save method to the model since it’s an instance of Yuga\Database\Elegant\Model, When the save method is called, It will try to insert or update the database table depending on the bound model
Form Validation
Forms in view-models are validated automatically for required field validations. This means, if a form is submitted to the server, the framework will look for the form, validate it (making sure every field has a value basically) then bind it to the appropriate model, then provide it as an Argument to the onPost method as below:
You can just call the save method on the $model variable since $model is already a valid Elegant Model.
Sometimes you want to customize how the validation is done, well, yuga has your back already, you just have to do the following in your model
If the above model is the one bound to the form, yuga will run its validate method instead of the default. Be warned though, when you run a custom validation, you will have to do it for every form field not just on the one field you want to customize.
namespace App\ViewModels;
use App\Models\User;
class UserViewModel extends App
{
/**
* The user model instance.
*/
protected $user;
/**
* Create a new UserViewModel ViewModel instance.
*
* @param User $user
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
}
namespace App\ViewModels;
class UserViewModel extends App
{
/**
* Create a new UserViewModel ViewModel instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Handle any form data that has been submited
*/
public function onPost()
{
}
/**
* Load or / manupulate data when its a get request
*/
public function onGet()
{
}
/**
* Load or / manupulate data before the page loads and feed it to the page
*/
public function onLoad()
{
}
}
<?php
namespace App\ViewModels;
class UserViewModel extends App
{
/**
* Create a new UserViewModel ViewModel instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Handle any form data that has been submited
*/
public function onPost()
{
}
/**
* Load or / manupulate data when its a get request
*/
public function onGet()
{
}
/**
* Load or / manupulate data before the page loads and feed it to the page
*/
public function onLoad()
{
// this will change the bound model to what is defined
$this->setModel(['form' => new App\Models\User]);
}
}
<?php
namespace App\ViewModels;
use Yuga\Models\ElegantModel;
class MyViewModel extends App
{
/**
* Create a new MyViewModel ViewModel instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Handle any form data that has been submited
*/
public function onPost($model)
{
}
/**
* Load or / manupulate data when its a get request
*/
public function onGet()
{
}
/**
* Load or / manupulate data before the page loads and feed it to the page
*/
public function onLoad()
{
}
}
/**
* Handle any form data that has been submited
*/
public function onPost($model)
{
$model->save();
}
/**
* Handle any form data that has been submited
* This $model argument is a valid Database Model
*/
public function onPost($model)
{
}
<?php
namespace App\Models;
use Yuga\Database\Elegant\Model as Elegant;
class User extends Elegant
{
/**
* Use this method to run a customized validation on a model bound to a view-model's form
*/
public function validate(): ?array
{
return [
'email' => 'required|email|unique' // blah blah
];
}
}