Yii2 - fucntions in controller or model - model-view-controller

I have such function in the controller
public function actionNext(){
$category = $this->getCategory();
$not_finished = $this->getQuestionFromCategory($category);
if(!empty($not_finished)){
$next_question_id = getNextQuestionId();
$this->updateNextQuestion();
}
else{
addNextCategory();
}
}
My question is: all fuctions
getCategory
getQuestionFromCategory
getNextQuestionId
updateNextQuestion
addNextCategory
from the example should be in model or controller too (all functions is the requests to the db).

Normally the function related to the db are in model, tipically the model extend the active record and for this contain also the related sql/schema/model related function. in your case is think the function getCategory and probably getQuestionFormCategory
The function related to service for support controller action are place in controller. in your case genNextQuestionId, updateNewQuestion, addNextQuestion.
The main rules is: what's regarding the structural knwoledge of an entity is in model, what's regarding tactical behaviuor is in controller.
Obviously the part related to sort and find are placed in ...search class.

Related

Laravel route difference between {id} vs {tag}

I am new in Laravel pardon me if question is silly. I have seen a doc where they used
For get request
Route::get("tags/{id}","TagsController#show");
For put request
Route::put("tags/{tag}","TagsController#update");
What is the difference and benefit between this ? I understood 1st one, confusion on put route.
There’s no real difference as it’s just a parameter name, but you’d need some way to differential parameters if you had more than one in a route, i.e. a nested resource controller:
Route::get('articles/{article}/comments/{comment}', 'ArticleCommentController#show');
Obviously you couldn’t use just {id} for both the article and comment parameters. For this reason, it’s best to use the “slug” version of a model for a parameter name, even if there’s just one in your route:
Route::get('articles/{article}', 'ArticleController#show');
You can also use route model binding. If you add a type-hint to your controller action for the parameter name, Laravel will attempt to look up an instance of the given class with the primary key in the URL.
Given the route in the second code example, if you had a controller that looked like this…
class ArticleController extends Controller
{
public function show(Article $article)
{
//
}
}
…and you requested /articles/123, then Laravel would attempt to look for an Article instance with the primary key of 123.
Route model binding is great as it removes a lot of find / findOrFail method calls in your controller. In most instances, you can reduce your controller actions to be one-liners:
class ArticleController extends Controller
{
public function show(Article $article)
{
return view('article.show', compact('article'));
}
}
Generally there's no practical difference unless you define a custom binding for a route parameter. Typically these bindings are defined in RouteServiceProvider as shown in the example in the docs
public function boot()
{
parent::boot();
Route::model('tag', App\Tag::class);
}
When you bind tag this way then your controller action can use the variable via model resultion:
public function update(Tag $tag) {
// $tag is resolved based on the identifier passed in the url
}
Usually models are automatically bound so doing it manually doesn't really need to be done however you can customise resolution logic if you do it manually
Normal way
Route::get("tags/{id}","TagsController#show");
function($id)
{
$tag = Tag::find($id);
dd($tag); // tag
}
With route model bindings
Route::put("tags/{tag}","TagsController#update");
function(Tag $tag) // Tag model binding
{
dd($tag); // tags
}
ref link https://laravel.com/docs/5.8/routing#implicit-binding
It's just a convention. You can call it all you want. Usually, and {id} refers to the id in your table. A tag, or similarly, a slug, is a string value. A tag could be 'entertainment' for video categories, while 'my-trip-to-spain' is a slug for the description of a video.
You have to chose the words what you are comfortable with. The value will be used to find in your database what record is needed to show the correct request in the view. Likewise you can use video/view/{id}/{slug} or any combination thereof.
Just make sure your URLs don't get too long. Because search engines won't show your website nicely in search results if you do. Find the balance between the unambiguous (for your database) and logic (for your visitors).
Check this out: Route model bindings
Use id, Laravel will get the id from route, and it will be the tag's id, it is integer.
function show($id) {
$tag = Tag::find($id);
}
Use tag, Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
In URL, your tag parameter is integer, however in your controller action $tag will be a model object:
function action(Tag $tag) {
$tag->name;
}
So you don't need to get the $tag by eloquent in your controller action. You just need to specify it is From model Tag $tag
It will do it automatically.

Where should I be saving a model in Laravel MVC?

I'm trying to get a more concrete understanding of MVC and keeping the controller layer as thin as possible.
One thing I keep asking myself is "Where should I call modelname->save()?"
Looking at the Laravel documentation, they set data to the model and call save in the controller which doesn't seem right...
<?php
namespace App\Http\Controllers;
use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class FlightController extends Controller
{
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
This is quite a simple example and might be why they do it all in the controller.
From my understanding and everything I've been reading, all business logic should sit inside the model, the controller is responsible for "traffic control" between the view and the model.
So would I be calling save inside the model itself? or should I be using a service layer?
Here is my current problem with example data.
I am updating the status of a model. The row already exists in the DB. I use PATCH /route/ to get to the controller method. From there I get the model.
class TimecardController extends Controller {
...
public function markAsPass(Request $request, $id) {
$test = Test::findOrFail($id);
//I don't think this is the corect way
//$test->status = "passed";
//$test->markedBy = "Teacher123";
//$test->save();
$test->passed();
...
return redirect($redirect_url);
}
}
class Test extends Model {
...
public function passed() {
$this->status = "passed";
//would I call save here?
//$this->save();
}
}
Do I take an approach like above? Or do I create a service layer where I would use the model instance to call the model functions and then call save on the model?
//in service class
public function makeTestAsPassed($test){
$test->passed();
$test->save();
}
Please let me know if any claification is needed.
You’re right in that business logic belongs in models. If you take a “resourceful” approach to your applications (in that you create controllers around entities) then you’ll find that your controller actions seldom call more than one model method.
Instead of calling save(), you can call create() and update() methods on your model. In your store() controller action, you can create a new entity with one line like this:
public function store(CreateRequest $request)
{
$model = Model::create($request->all());
}
And update an existing model in an update() action like this:
public function update(UpdateRequest $request, Model $model)
{
$model->update($request->all());
}
When it comes to business logic, you can call other methods on your models, too. To use resourceful controllers, you don’t have to have a model that relates to a database table.
Take shipping an order. Most people would be tempted to put a ship() method in an OrderController, but what happens when you ship an order? What entity could shipping an order result in? Well, you’d be creating a shipment, so that could instead be a store() method on an OrderShipmentController. This store() method could then just call a ship() method on your Order model:
class OrderShipmentController extends Controller
{
public function store(ShipOrderRequest $request, Order $order)
{
$order->ship();
}
}
So as you can see, with resourceful controllers and route–model binding, you can have “skinny controllers” with your application’s business logic living in your models.
MVC is designed for ease of maintenance.
The approach you don't recognize as "good" is the proper approach. All data handling related to business logic goes in the controller. Otherwise, a different coder will be confused as he/she would not find the data manipulation logic in the controller code.
Your thin controller goal defeats MVC.
Also note the model code is purposed to be thin as it is a place to define the database schema as mirror image to the database tables.
MVC is not object oriented abstration. MVC is a structure for code maintenance uniformity.

Different state for Eloquent model fields depending on current user in laravel

I have the model:
class Task extends Model {
}
with some fields
protected $fillable = ['message', 'due_time', 'status', 'etc...'];
I've added custom function:
public function getEditableStateFor{AttributeName}
In my helper function I check that if
method_exists($class, 'getEditableStateForField1')
than I allow to edit this field depending on boolean value returned from this function.
Example:
if( ! $class->getEditableStateForField1() ) {
return "You can not edit field field1";
}
Here is how looks like some functions in Task:
private function isCreator() {
$user = Auth::user();
if($user) {
return $user->id === $this->creator_id;
}
return false;
}
public function getEditableStateForMessage() {
return $this->isCreator();
}
public function getEditableStateForDueTime() {
return $this->isCreator();
}
Is this a good way to do it or it is very bad design because of hidden dependency on Auth::user()?
What is a better way?
I do not want to put this logic inside controllers because this logic propagates to another models and is universal across application.
I'm like you and like to have Models that contain as much of the business logic as possible while remaining totally free of depencies on the "web" part of the application, which I believe should stay in Controllers, Request objects, etc. Ideally, Models should be easily usable from command line interfaces to the application, from within the Tinker REPL, and elsewhere while still guaranteeing data integrity and that business rules are observed.
That said, it seems the Laravel creators had slightly different ideas, hence the Auth facade being easily available in the model.
What I would likely do is add a parameter of type User to the getEditableStateFor series functions, and then in turn pass that parameter to isCreator ($user) and elsewhere. That also frees you up to be able to allow associated users to edit each other's Tasks if that ever became a desired feature in the future.
Edit: another, perhaps better or perhaps worse, is to have an instance method like setCurrentUser ($user) then use setFieldNameAttribute methods so that the controller doesn't have to check the editability of fields, keeping that the model's responsibility. Then you could call the getEditableStateFor methods, which now check for the current user set by the above method (maybe falling back to Auth::user() or throwing a helpful error), inside the setter.

how to achieve MVC in my Zend Framework

currently i am doing a project in zend the way i am doing is working perfectly but i am sure its not the way i am suppose to do i mean i am not following MVC and i want to apply MVC in my zend app.
i am pasting code of one simple module which will describe what i am doing .kindly correct me where i am making faults.
my controller
class ContactsController extends Zend_Controller_Action{
public function contactsAction(){
if(!Zend_Auth::getInstance()->hasIdentity()){
$this->_redirect('login/login');
}
else{
$request = $this->getRequest();
$user = new Zend_Session_Namespace('user');
$phone_service_id = $user->p_id;
$instance = new Contacts();
$select = $instance->Get_Contacts($p_id);
$adapter = new Zend_Paginator_Adapter_DbSelect($select);
$paginator = new Zend_Paginator($adapter);
.
.
//more code
}
plz note this 2 line in my controller
$instance = new Contacts();
$select = $instance->Get_Contacts($pid);
this is my contacts class in models
class Contacts extends Zend_Db_Table{
function Get_Contacts($p_id){
$DB = Zend_Db_Table_Abstract::getDefaultAdapter();
$select = $DB->select()
->from('contact', array('contact_id','contact_first_name','contact_mobile_no','contact_home_no','contact_email','contact_office_no'))
->where('pid = ?', $p_id)
->order('date_created DESC');
return $select;
}
}
after this i simple assign my result to my view.
note please
as its working but there is not private data members in my class,my class is not a blue print.there are no SETTERS AND GETTERS .how can i make my code that best suits MVC and OOP??
The most simple answer: you are already almost MVC. You use a Zend_Controller_Action to grab some data and pass this on to a view layer where you render the html. The only missing part is your model, which is mixed up between the controller and your data gateway (where you implemented a table data gateway pattern, that Zend_Db_Table thing).
I gave a pretty thorough explanation in an answer to another question how I'd properly set up the relations between Controller and Model. I also combined this with a Form, to handle data input, filtering and validation. Then to bundle some common functions, I introduced a Service layer between the Model and Controller.
With the controller, you perform some actions (list all my contacts, create a new contact, modify a contact) and the model is purely containing the data (id, name, phone, address). The service helps to group some functions (findContactByName, findContactById, updateContactWithForm).
If you know how to split Controller, Mode, Form and Service, your controller can become something like this:
class ContactsController extends Zend_Controller_Action
{
public function indexAction ()
{
if (!$this->hasIdentity()) {
$this->_redirect('login/login');
}
$service = new Application_Service_Contacts;
$contacts = $service->getContacts();
$paginator = $service->getPaginator($contacts);
$this->view->paginator = $paginator;
}
protected function hasIdentity ()
{
return Zend_Auth::getInstance->hasIdentity();
}
}
It is your personal taste what you want to do in your controller: I'd say you put as less as possible in your controllers, but you need to keep the control. So: a call to get data happens in the controller, retrieving this data happens somewhere else. Also: a call to convert a dataset into something else happens in the controller, the conversion happens somewhere else.
This way you can change the outcome in controllers extremely fast if you provided enough methods to your service classes to fetch the data. (Note I took the Zend_Auth to another function: if you have other actions, you can use this same function. Also, if you want to change something in your authentication, you have one place where this is located instead of every action in the controller)
keep one thing in mind when u learn new technology so first read thier own documentation. No one can explain better than them. Its hard to understand firstly but when you study it you will usedto and than u will love it like me Zend Offical Site

Should I call redirect() from within my Controller or Model in an MVC framework?

I'm using the MVC PHP framework Codeigniter and I have a straight forward question about where to call redirect() from: Controller or Model?
Scenario:
A user navigates to www.example.com/item/555. In my Model I search the item database for an item with the ID of 555. If I find the item, I'll return the result to my controller. However, if an item is not found, I want to redirect the user somewhere. Should this call to redirect() come from inside the model or the controller? Why?
No your model should return false and you should check in your controller like so:
class SampleModel extends Model
{
//Construct
public function FetchItem($id)
{
$result = $this->db->select("*")->from("table")->where("item_id",$id)->get();
if($result->num_rows() == 0)
{
return false;
}
//return result
}
}
and within your controller do:
function item($id)
{
$Item = $this->SampleModel->FetchItem($id);
if(!$Item)
{
redirect("class/error/no_item");
}
}
Models are for data only either return a standard result such as an key/value object or a boolean.
all logic should be handled / controlled by the Controller.
Models are not page specific, and are used globally throughout the whole application, so if another class / method uses the model, it might get redirect to the incorrect location as its a different part of your site.
It seems like the controller would be the best place to invoke your redirect because the controller typically delegates calls to the model, view, or in your case, another controller.
However, you should use whatever makes the most sense for your application and for what will be easier to maintain in the future, but also consider that rules do exist for a reason.
In short, if a coworker were to try to fix a bug in your code, what would the "reasonable person" standard say? Where would most of them be most likely to look for your redirect?
Plus, you said you're returning the result to your controller already... perhaps that's where you should make your redirect...

Resources