I am looking for a way how to attach a behavior to model displayed in a grid view in Yii Framework. The grid view is using CActiveDataProvider and I need every $data element to have a behavior attached to it. The model shouldn't attach the behavior after construct, since it is related to the grid view only.
Thanks
You can create the following class to use to create a data provider.
ActiveDataProvider extends CActiveDataProvider{
public function getData(){
$data = parent::getData();
foreach($data as &$model){
$model->attachBehavior('aName', new mybehavior());
}
return $data;
}
}
Another option (instead of creating a CActiveDataProvider override as suggested in another answer) is to do all your model querying ahead of time and attach your behaviors in your controller. Then pass to a CArrayDataProvider.
Hmm, thinking about it, I like the other approach better :-) I'll leave this for completeness sake.
Related
I have a problem with code using Laravel. I define an attribute in a model to get a list. It takes a lot of time. I use this model in a controller. Follow the code:
protected $appends=["consume_info"];
public function getConsumeInfoAttribute(){
//query a lot of information from mysql
}
I'm wondering if there is an attribute in Controller or Model to avoid a query with mysql in model.
Is there a setting to tell Laravel when to load this appended attribute or not?
Why not just remove the consume_info from $appends array. You will get the $model->consumer_info and that too only when you need this.
How do you optimize the DB queries made by sonata admin in the list and edit views ?
i LeftJoined some queries that i made using the querybuilder in my entity repository , this already helped a lot, brought my queries down from 100+ to about 22.
But the remaining queries are the ones that happen automatically by using the formbuilder and listmapper.
Is there anyway i can further optimize the queries made by those classes ?
im not even sure at this point where the queries are made... i tried to overwrite the findBy, findAll methods of the repository but they seem to use something like
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
return $persister->load($criteria, null, null, array(), 0, 1, $orderBy);
Not sure how i can add a join statement to that...
And i guess it's not just in the sonata admin but also for queries that i use in the front-end that use the built-in EntityRepository find, findAll, findOneBy etc... functions.
In your admin class override the createQuery() method like this :
public function createQuery($context = 'list')
{
$query = parent::createQuery($context);
$query
->addSelect(...)
->leftJoin(...) // use $query->getRootAlias() here
;
return $query;
}
For the edit view it's a little bit more tricky because you need to override the sonata controller by extending Sonata\AdminBundle\Controller\CRUDController and then override the editAction() method.
The controller name can be specified in the third argument of the service declaration.
I know it some month but if it can help someone :
I didn't wanted to change the controller so i take a look inside the code.
In your admin class, you can override the function getObject($id):
// code from sonata admin class
public function getObject($id)
{
$object = $this->getModelManager()->find($this->getClass(), $id);
foreach ($this->getExtensions() as $extension) {
$extension->alterObject($this, $object);
}
return $object;
}
Then put your own code in and return your object.
That's all, no need to override sonata controller.
This question already has answers here:
How should a model be structured in MVC? [closed]
(5 answers)
Closed 9 years ago.
In the ideal world one should not rely on singletons, the model in the controller and model inside the view would be 2 different instances. The problem arises when the controller sets a state and the view presentation depends on that state. For example:
class MyController extends Controller {
public function __construct(ModelUsers $cModel)
{
$this->model = $cModel;
}
public function action_Search($username) {
$this->model->filterByUsername($username);
}
}
class MyView extends View {
public function __construct(ModelUsers $vModel)
{
$this->model = $vModel;
}
public function users() {
return $this->model->getUsers();
}
}
How to share data between the controller model and the view model?
Starting from basics
A view requests from the model the information that it needs to generate an output representation to the user.
It means the view should be only responsible for showing the information. Just for that. You can also do some things like triming, chaning text size etc. but you shouldn't do some countings there or more complicated operations.
A model notifies its associated views and controllers when there has been a change in its state. This notification allows the views to produce updated output, and the controllers to change the available set of commands.
Model should be responsible for doing data operations. You can use it for example to get the records from database. It just be responsible for data handling.
A controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document).
Controler is kind a proxy between model and view. You get there params and according to this params you set proper action of your controller. This action should create correct model object and use it to get data then assign to the view.
I've never used singleton in models. If you need some classes that would help MVC structure you can use helpers and as Hast suggested Registry pattern. I'm not a fan of using singleton.
You may also want to look at When to use singleton
So your question.
Controler -> model = Passing data via arguments of model's methods
Model -> controler = If reference then just work on it, if argument then do something and return result
Controler -> view = assign proper data to be viewed.
View->controller = go to special url to make data or use ajax request to retrieve it.
You can use Registry or Dependency injection instead.
Also in some cases you may pass some data to your view class as array. Like this:
class MyView extends View {
private $data = array();
public function __construct($data)
{
$this->data = $data;
}
public function users() {
return $this->data['model']->getUsers();
}
}
Of course you have to pass model when you caling the View class from your controller (or wherever you make call).
I'm asking for a best practice to create an adhoc model. I want to create a model 'menu' which does not come from a database table but hardcoded in the code instead.
The reason is that i can call the menu model from two different view/layouts.
I tried this as a starting point.
/model/Menu.php
class Menu extends CModel
{
public getMenu() {
return array('home'=>'home/index',
'product'=>'product/index',
'order'=>'order/index',
);
}
}
Do you think this is bad or is there a better way to do this?
Thanks
I think it should be a widget, not model. Or even partial view. Depends on how you going to use it.
I'd suggest creating it as a class just like you did, but I wouldn't extend CModel to avoid the unnecessary overhead. Something like:
class Menu{
public static getMenu() {
return array('home'=>'home/index',
'product'=>'product/index',
'order'=>'order/index',
);
}
}
I have a controller/model for projects. so this controls the projects model, etc, etc. I have a homepage which is being controlled by the pages_controller. I want to show a list of projects on the homepage. Is it as easy as doing:
function index() {
$this->set('projects', $this->Project->find('all'));
}
I'm guessing not as I'm getting:
Undefined property: PagesController::$Project
Can someone steer me in the right direction please,
Jonesy
You must load every model in the controller class by variable $uses, for example:
var $uses = array('Project');
or in action use method
$this->loadModel('Project');
In my opinion the proper way to do this is add a function to your current model which instantiates the other model and returns the needed data.
Here's an example which returns data from the Project model in a model called Example and calls the data in the Example controller:
Using Project Model inside Example Model:
<?php
/* Example Model */
App::uses('Project', 'Model');
class Example extends AppModel {
public function allProjects() {
$projectModel = new Project();
$projects = $projectModel->find('all');
return $projects;
}
}
Returning that data in Example Controller
// once inside your correct view function just do:
$projects = $this->Example->allProjects();
$this->set('projects', $projects);
In the Example view
<?php
// Now assuming you're in the .ctp template associated with
// your view function which used: $projects = $this->Example->allProjects();
// you should be able to access the var: $projects
// For example:
print_r($projects['Project']);
Why is this "better" practice than loading both models into your controller? Well, the Project model is inherited by the Example model, so Project data now becomes part of the Example model scope. (What this means on the database side of things is the 2 tables are joined using SQL JOIN clauses).
Or as the manual says:
One of the most powerful features of CakePHP is the ability to link relational mapping provided by the model. In CakePHP, the links between models are handled through associations.
Defining relations between different objects in your application should be a natural process. For example: in a recipe database, a recipe may have many reviews, reviews have a single author, and authors may have many recipes. Defining the way these relations work allows you to access your data in an intuitive and powerful way. (source)
For me it's more reasonable to use requestAction. This way the logic is wrapped in the controller.
In example:
//in your controller Projects:
class ProjectsController extends AppController {
function dashboard(){
$this->set('projects', $this->Project->find('all'));
}
$this->render('dashboard');
}
Bear in mind that you need to create dashboard.ctp in /app/views/projects of course.
In the Page's dashboard view (probably /app/views/pages/dashboard.ctp) add:
echo $this->requestAction(array('controller'=>'projects', 'action'=>'dashboard'));
This way the logic will remain in the project's controller. Of course you can request /projects/index, but the handling of the pagination will be more complicated.
more about requestAction(). but bear in mind that you need to use it carefully. It could slow down your application.