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
}
. . .
}
?>
Related
For historical reasons, my pattern of running databases using Symfony is mixed. That is, the query uses DBAL and the insert uses ORM. Now you need to write a lot of data to the database. The flush in ORM can help me achieve business at the lowest cost.
All flush operations have been removed from the project. Put it in the __destruct of the controller.
However, doing so will cause DBAL to not find the latest changed data. Of course, these data ORMs can be obtained normally.
This is a very difficult problem. I hope to get guidance.
class BaseController extends Controller
{
public function __destruct()
{
$this->getDoctrine()->getManager()->flush();
}
public function indexAction()
{
$model = new CompanyModel();
$model->install(['company_name' => '1234']);
$model->update(['company_name' => 'abcd'], $model->lastInsertId);
}
}
class CompanyModel extends BaseController
{
public function validate($data, $id = false)
{
$this->entityManager = $this->getDoctrine()->getManager();
if(empty($id)){
$this->company_class = new Company();
}else{
if(!$this->is_exist($id)){
return false;
}
$this->company_class = $this->entityManager->getRepository(Company::class)->find($id);
}
if(array_key_exists('company_name', $data)){
$this->company_class->setCompanyName($data['company_name']);
}
if(self::$error->validate($this->company_class)){
return false;
}
return true;
}
public function insert($data)
{
if(!$this->validate($data)){
return false;
}
$this->company_class->setCreateAt(new \DateTime());
$this->entityManager->persist($this->company_class);
//$this->entityManager->flush();
$this->lastInsertId = $this->company_class->getId();
return true;
}
public function update($data, $id)
{
if(empty($id)){
self::$error->setError('param id is not null');
return false;
}
if(!$this->validate($data, $id)){
return false;
}
$this->company_class->setUpdateAt(new \DateTime());
//$this->entityManager->flush();
return true;
}
public function is_exist($id)
{
return $this->get('database_connection')->fetchColumn('...');
}
}
The final result of executing indexAction company_name is 1234; $ model-> update() was not executed successfully. The reason is that the $this-> is_exist() method that took the DBAL query did not find the ORM insert but did not flush the message.
Unchanging conditions,run
$this->entityManager->getRepository(Company::class)->find($id);
Is successful。
The problem is not the entity manager or dbal, as far as I can tell, but the usage of an anti-pattern, which I would call ... entanglement. What you should strive for is separation of concerns. Essentially: Your "CompanyModel" is an insufficient and bad wrapper for the EntityManager and/or EntityRepository.
No object should know about the entity manager. It should only be concerned with holding the data.
The entity manager should be concerned with persistence and ensuring integrity.
The controller is meant to orchestrate one "action", that can be adding one company, editing one company, batch-importing/updatig many companies.
Services can be implemented, when actions become to business-logic-heavy or when functionality is repeated.
(Note: the following code samples could be made way more elegant with using all the features that symfony provide, like ParamConverters, the Form component, the Validation component, I usually wouldn't write code this way, but I assume everything else would go way over your head - no offence.)
handling actions in the controller
controller actions (or service actions, really) are when you look at your problem from the task perspective. Like "I want to update that object with this data"). That's when you fetch/create that object, then give it the data.
use Doctrine\ORM\EntityManagerInterface;
class BaseController extends Controller {
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
public function addAction() {
$company = new Company(['name' => '1234']); // initial setting in constructor
$this->em->persist($company);
// since you have the object, you can do any changes to it.
// just change the object
$company->update(['name' => 'abcd']); // <-- don't need id
// updates will be flushed as well!
$this->em->flush();
}
public function editAction($id, $newData) {
$company = $this->em->find(Company::class, $id);
if(!$company) {
throw $this->createNotFoundException();
}
$company->update($newData);
$this->em->flush();
}
// $companiesData should be an array of arrays, each containing
// a company with an id for update, or without an id for creation
public function batchAction(array $companiesData) {
foreach($companies as $companyData) {
if($companyData['id']) {
// has id -> update existing company
$company = $this->em->find(Company::class, $companyData['id']);
//// optional:
// if(!$company) { // id was given, but company does not exist
// continue; // skip
// // OR
// $company = new Company($companyData); // create
// // OR
// throw new \Exception('company not found: '.$companyData['id']);
// }
$company->update($companyData);
} else {
// no id -> create new company
$company = new Company($companyData);
$this->em->persist($company);
}
}
$this->em->flush(); // one flush.
}
}
the base controller should handle creating objects, and persisting it, so very basic business logic. some would argue, that some of those operations should be done in an adapted Repository for that class, or should be encapsulated in a Service. And they would be right, generally.
the entity handles it's internal state
Now, the Company class handles its own properties and tries to stay consistent. You just have to make some assumptions here. First of all: the object itself shouldn't care if it exists in the database or not. it's not its purpose! it should handle itself. Separation of concerns! The functions inside the Company entity should concern simple business logic, that concerns its INNER state. It doesn't need the database, and it should not have any reference to the database, it only cares about it's fields.
class Company {
/**
* all the database fields as public $fieldname;
*/
// ...
/**
* constructor for the inital state. You should never want
* an inconsistent state!
*/
public function __construct(array $data=[]) {
$this->validate($data); // set values
if(empty($this->createAt)) {
$this->createAt = new \DateTime();
}
}
/**
* update the data
*/
public function update(array $data) {
$this->validate($data); // set new values
$this->updateAt = new \DateTime();
}
public function validate(array $data) {
// this is simplified, but you can also validate
// here and throw exceptions and stuff
foreach($array as $key => $value) {
$this->$key = $value;
}
}
}
some notes
Now, there should be NO use case, where you get an object to persist and at the same time an update - with an id - that refers to the new object ... unless that object was given the id beforehand! HOWEVER. If you persist an object, that has an ID and you call $this->em->find(Company::class, $id) you would get that object back.
if you have many relations, there are always good ways to solve this problem without destroying separation of concerns! you should never inject an entity manager into an entity. the entity should not manage its own persistence! nor should it manage the persistence of linked objects. handling persistence is the purpose of the entity manager or entity repository. you should never need a wrapper around an object just to handle that object. be careful not to mix responsibilities of services, entities (objects) and controllers. In my example code, I have merged services and controllers, because in simple cases, it's good enough.
Thanks for watching my first question.
I have something confused.
How could I write the operations of database into database and don't write the function in every Controller?
I have considered middleware and find that must change my route register style.
my Route is this:
Route:resource('province','\\Modules\\Info\\Controllers\\P_ProvinceController');
Dose it has some awesome methods replace this?
public function Store(Request $request)
{
$params = $request->input('data');
$params['CreateID'] = Auth::user()->id;
$params['CreateName'] = Auth::user()->name;
$params['CreateTime'] = Carbon::now();
$province = P_ProvinceModel::Create($params);
$params['Pro_Is_Del'] = 1;
$log_info['table'] = $province->getTable();
$log_info['type'] = "create";
$log_info['user'] = Auth::user()->name;
$log_info['datetime'] = Carbon::now();
LogModel::create($log_info);
if($province){
return response()->json(array(
'status' => 200,
'msg' => '新增成功',
'data' => $province
));
}else
return response()->json(array(
'status' => 500,
'msg' => '保存失败',
));
}
Thanks.
Here is how I solved across model functionality
First Create a Trait that does what you want on save.
<?php
namespace App\Models\Observers;
trait CreatedByObserver
{
public static function bootCreatedByObserver(){
/** Simply means that whenever this model is creating a model do: */
static::creating(function($model){
if(auth()->check()){
$responsiblePerson = auth()->user()->first_name . " " . auth()->user()->last_name;
} else {
$responsiblePerson = "system";
}
/** You can set any model variables within */
$model->created_by = $responsiblePerson;
});
}
}
In there do all you need to do when a record is saved/created/updated/deleted
Then In all Models you want this behaviour used add the trait.
Check them out here : https://laravel.com/docs/5.2/eloquent#events
As far i understand your question, you are asking for way to make your controller an abstract type, i.e. controller just need to handle the route and view not any other things(like database,application logic etc) which is the philosophy of the laravel Framework.
To make your controller abstract (meaning of abstract as explained aboave),
first you need to understand, "What your application logic are and what your database logic are ?"
when you understand these two things then, you can easily separate your aapplication logic and databasse logic from your controller.
For example :
For keeping your Application logic you can make service folder in your root of your project also you can make folder name 'Dao' (Database access object) in the same path of service . You need to keep these folder in autoload from your composer. Just make class for service and your Dao.
And now your application follow will be,
First Route, will hit controller then controller will need to call some method in service and then service will call the respective DAO . method.
Example :
Controller/YourController.php
Class YourController extends Controller {
public function Store(Request $request,yourservice,$yourService)
{
$this->myservice = $yourservice;
$this->myservice->store('your inputs request');
return $something ;
}
}
service/yourService.php
Class yourService {
public function store($yourinputs,yourDao $mydao){
$this->mydao = $mydao;
//you can use your application logic here
return $this->mydao->create($yourinputs);
}
And now the turn is for DAO :
dao/yourdao.php
use model // use your model here .
class yourDao {
public function create($yourdata,yourmodel $model){
$this->model = $model;
return $this->model->create($yourdata);
}
}
Now, you can see the controller just save the data in database, but don't know how it is saving the data and what are the application logic.
This explanation is just a simple way of doing project to make a controller abstract. There are other various ways of doing this. For example you can see Repository Design Pattern , which also used by laravel core .
Hope this explanation will not bore anyone . :) Happy coding .
My Zend Framework 2 Application has a view whereby I display a log of events, in a simple table format. I use several basic View Helpers to manipulate the presentation of the data in the table, but on these existing View Helpers all of the logic is contained to the View Helper itself, e.g:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class GetSystemName extends AbstractHelper
{
public function __invoke($val)
{
if ($val == 0){
return 'Something';
}
if ($val == 1){
return 'Something else';
}
}
}
My requirement is to build a function GetUserName to accept user_id and perform a check on the database to display the User's name, as the ID is of no value to the person using the system.
The way I see it I can either:
A) Start a new query from within the View Helper to return what I need or
B) Use a function called getUser() from within the 'User' Module / UserTable class.
The code for B is:
namespace User\Model;
use Zend\Db\TableGateway\TableGateway;
class UserTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
//..other functions
public function getUser($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
What is the best option? And how would I implement it?
Apologies if this is a basic questions I am quite new to MVC and Zend.
In the model-view-controller pattern, the view should not be aware of the model layer. Information from the models are injected into the view through the controller.
Having your view helper call the getUser() method in your model breaks this pattern.
So, what do you do?
Have your controller get the user information into the view:
// controller
$userId = $this->params()->fromQuery("userID");
// or from session ID if this is a private profile page
// You might want some validation, too...
$userTable = $this->getServiceLocator()->get("UserTable");
// or whatever you've configured in the service config for this
$user = $userTable->getUser($userId);
// if this is a public profile page, you might want to
// exclude some fields like "password" so they don't
// accidentally get into the view
$view = new ViewModel();
$view->setVariable("user", $user);
return $view;
Then in the view.phtml you just do:
<?php $this->GetSystemName($user->whateverField); ?>
My modules all extend Backend controller which checks if loggedin and redirects appropriately, now I have Class FrontEnd [responsible for pages like login, signup etc] which extends My_Controller.
how can I create an exemption for FrontEnd to be able to access the modules since it needs their methods to perform tasks.
this is what I have currently ....
class Backend_Controller extends MY_Controller {
// --------------------------------------------------------------------------
public function __construct() {
parent::__construct();
$this->checkLoggedIn();
}
// --------------------------------------------------------------------------
public function checkLoggedIn() {
//check if loggedin
if (!$this->auth->loggedIn()) {
//implement exclusion for FrontEnd class to allow access to methods though not logged in
if (!the class trying to access == 'FrontEnd') { //how to get refferal page
return;
} else {
redirect('login');
}
}
}
// --------------------------------------------------------------------------
}
how can I get "the class trying to access == 'FrontEnd"
Edditional Info:
the FrontEnd class generates a form which has an
enter code hereaction= router->fetch_class() will result in someMoudleName ,
but what i want is for it to be able to detect that the action is coming from FrontEnd Class
This will depend on your url but u can do something like this..
function getNameOfOriginatingClass{
$this->load->library('user_agent');
$previous_url = $this->agent->referrer();
$url_segments = explode('/',$previous_url);
echo '<pre>';print_r($url_segments);
}
after printing this result u can see your link broken into parts in an array.. Normally the $url_segments[3] or $url_segments[4] will contain your previous function name and previous one will contain previous class name depending upon your url.
I have been working on my own library/framework for the learning experience for a while. MVC is one of those things that took me a while to really understand but I do finally "Get it".
Below is some sample code for a basic MVC setup in PHP. I think I am in the right direction so far, where I need a little help is down in the "Example controller" near the bottom, you will see where I can create a view, I just need to figure out how to best get my data from a model file into that controller class. Please help with example code if you can, hopefully I am making sense.
Also I am welcome to any comments/suggestions on any of the code
Abstract Controller class...
/**
* MVC Example Project
*/
/**
* Extend this class with your Controllers
* Reference to the model wrapper / loader functions via $this->model
* Reference to the view functions via $this->view
*/
abstract class Core_Controller {
protected $view;
protected $model;
function __construct($dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
Abstract Model class...
/**
* Extend this class with your models and reference to the database object via $this->$db
*/
abstract class Core_Model {
protected $db;
protected $session;
function __construct($dependencyContainer) {
$this->db = $dependencyContainer->get(database);
$this->session = $dependencyContainer->get(session);
}
}
View class, might make it abstract as well...
class Core_View {
protected $data;
# Load a view file (views/$view.php);
# $param data this gets extracted and be thus be used inside the view
# When loading another view from inside the view file the data is 'cached' so you
# don't have to pass them again
public function load($view,$data = null) {
if($data) {
$this->data = $data;
extract($data);
} elseif($this->data != null) {
extract($this->data);
}
require(APP_PATH . "Views/$view.php");
}
public function set($data = null) {
if($data) {
$this->data = $data;
extract($data);
} elseif($this->data != null) {
extract($this->data);
}
}
}
Example putting it together...
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
public function profile()
{
$profileData = array();
$profileData = //GET from Model
$this->view->load('userProfile', $profileData);
}
}
?>
My suggestion is not to tie view and model to the controller at all. Let them be instantiable from controller code, just like any other classes. You can then get the model data (and pass it to the view) in standard object oriented way.
Will you use a Data access layer (DAL) / Object-relational mapping (ORM)? Take a look at Zend_Db, Doctrine or Propel
I'd say that you're missing the part of the application that manipulate your models. It could be your controller, but isn't a good practice. So we need a model mapper.
The best way to get model data from your controller is simply calling it. But generally we use a kind of "pointer" which knows how to populate your object model. This pointer is called "Mappers" (Data Mapper Pattern):
$MyModelMapper = new MyModelMapper();
$Profile = $MyModelMapper->getProfileById($id); // return Core_Model.
This function will perform a database query and will populate one specific model with the data. You could also get an array of objects for a "list" action for example.
Then you'll pass this model to your view.
I think you should take a look at the Zend Framewok quick start. It will give you some ideas.
See this question too: What's the difference between DAO and Data Mapper