I have Model_Group that extends ORM.
I have Controller_Group that gets a new ORM:
public function before()
{
global $orm_group;
$orm_group = ORM::factory('Group');
}
...and it has various methods that use it to get different subsets of data, such as...
public function action_get_by_type()
{
global $orm_group;
$type = $this->request->param('type');
$result = $orm_group->where('type', '=', $type)->find_all();
}
Then I have another controller (in a separate module) that I want to use to manipulate the object and call the relevant view. Let's call it Controller_Pages.
$orm_object = // Get the $result from Controller_Group somehow!
$this->template->content = View::factory( 'page1' )
->set('orm_object', $orm_object)
What is the best way to pass the ORM object from Controller_Group to Controller_Pages? Is this a good idea? If not, why not, and what better way is there of doing it?
The reason for separating them out into different controllers is because I want to be able to re-use the methods in Controller_Group from other modules. Each module may want to deal with the object in a different way.
This is the way I would do it, but first I would like to note that you shouldn't use global in this context.
If you want to set your ORM model in the before function, just make a variable in your controller and add it like this.
public function before()
{
$this->orm_group = ORM::factory('type');
}
In your Model your should also add the functions to access data and keep the controllers as small as possible. You ORM model could look something like this.
public class Model_Group extends ORM {
//All your other code
public function get_by_type($type)
{
return $this->where('type', '=', $type)->find_all();
}
}
Than in your controllers you can do something like this.
public function action_index()
{
$type = $this->request->param('type');
$result = $this->orm_group->get_by_type($type);
}
I hope this helps.
I always create an helper class for stuff like this
Class Grouphelper{
public static function getGroupByType($type){
return ORM::factory('Group')->where('type','=',$type)->find_all();
}
}
Now you're been able to get the groups by type where you want:
Grouphelper::getGroupByType($type);
Related
I'm using the Dunglas api-platform bundle (https://github.com/api-platform/api-platform) for a new app.
Setup and installation went fine, GET requests are working.
While trying to create new objects using POST requests, I received errors about having a private constructor. My models are all made using a private constructor, and using named constructors instead.
Ideally i'm either looking for a way to have the bundle call my Named constructors, ... or someone to tell me my approach is completely wrong.
Services.yml
services:
resource.player:
parent: "api.resource"
arguments: [ "Name\\Space\\Player" ]
tags: [ { name: "api.resource" } ]
Player Object
class Player
{
private $name;
private function __construct()
{
}
public static function withName($playerName)
{
$player = new Player();
$player->name = $playerName;
return $player;
}
public function getName()
{
return $this->name;
}
}
Settings are pretty much all out of the box, following the introduction and setup in the documentation. I've skimmed through the Factory thing briefly - hoping that i'd be able to use a factory to create the objects, allowing me to call my own named constructors - but that doesn't seem to do what i think it does.
Any input regarding the use, boundaries or the setup is well appreciated.
API Platform (like most Symfony and Doctrine related libraries) is not designed to work with immutable objects like this one.
I suggest to create a typical mutable Entity as suggested in the doc:
class Player
{
private $name;
public static function setName($playerName)
{
$this->name = $playerName;
}
public function getName()
{
return $this->name;
}
}
If you really want to keep your immutable model, you'll need to implement yourself the Symfony\Component\PropertyAccess\PropertyAccessorInterface and use a CompilerPass to make API Platform using your own implementation. You will probably need to submit a patch to API Platform and to the Symfony Serializer Component to update the reference of the given object too because currently, both serializers actually update the current object and will not use the new instance returned by your with method.
I strongly encourage you to switch to typical mutable entities.
I'm working on a web app using the Lithium Framework with a MongoDB database.
On one page of the application - I want to display data from multiple object types. I understand the concept of relationships (i.e. belongsTo, hasMany, etc.) between models. But, my questions has to do with Controller relationships.
For example, assume I have two objects named "People" and "Companies". I want to show specific information about Companies on a "people" view. I have done the following:
1) In the "People" model, I've added the following line:
public $belongsTo = array('Companies');
2) In the "PeopleController" file, I've also included a reference to the Companies Model, such as:
use app\models\Companies;
Now, within the PeopleController, I want to call a method in the CompaniesController file.
Do I access this by directly calling the CompaniesController file? Or, do I have to go thru the Company model.
In either case, I'll need help with the syntax. I'm having rouble figuring out the best way this should be called.
Thanks in advance for your help!
You should rethink your structure - you controller method should really grab all the resources you need for that view, it doesn't matter what they are.
So if you have a url '/people/bob' and you want to get the company data for Bob just add that to the view method of your People controller. Something like
People::first(array('conditions' => array('name' => 'Bob'), 'with' => 'Companies'));
You could instantiate a CompaniesController (maybe passing in $this->request to the 'request' option in the process) and then call the method in it. However, a better way to organize it is to move the common functionality from CompaniesController to Companies and call it from both places.
use app\models\Companies does not really make a "reference." It simply indicates that Companies really means app\models\Companies. I think an "alias" is a better way to think of it. See http://php.net/manual/en/language.namespaces.importing.php.
Example:
// in app/models/Companies.php
namespace app\models;
class Companies extends \lithium\data\Model {
public static function doSomething() {
// do something related to companies.
}
}
// in app/controllers/CompaniesController.php
namespace app\controllers;
use app\models\Companies;
class CompaniesController extends \lithium\action\Controller {
public function index() {
$result = Companies::doSomething();
return array('some' => 'data', 'for' => 'the view');
}
}
// in app/controllers/PeopleController.php
namespace app\controllers;
use app\models\Companies;
class PeopleController extends \lithium\action\Controller {
public function index() {
$result = Companies::doSomething();
return array('some' => 'data', 'for' => 'the view');
}
}
I know how to create a class the will allow me to instantiate it and use across my project. What I want to be able to do is have functions without instantiating classes. For example, I know how to do this:
$core = new core();
$val = $core->convertToMyNotation($anotherval);
But what I want is to be able to do this ANYWHERE in any view, class whatever:
$val = convertToMyNotation($anotherval);
Where would I place these functions in order to be able to do that?
best way to do it, create a public function in components/Controller.php
public function globalFunction(){
// do something here.
}
and access it anywhere by
$this->globalFunction();
You can define a static method as an option.
class core{
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
Then call it like so:
$val = core::convertToMyNotation($anotherval);
This requires no instantiation of the object to use. The only restriction is that you cannot use the $this property inside a static method.
Alternately, just define a file with your functions in it and include the file at some point early like, like within the boostrap script in your public_html/index.php file.
Edit: darkheir makes some good suggestions. Include such a class in your protected/components folder, and have it extend CComponent to gain some potentially useful enhancements.
By including the class in the protected/components folder, you gain the advantage of autoloading the class, by default.
There is no definitive question of your answer, it depends a lot on what the function will be doing!
If the function is performing some things specific to a model
(getting the last users, ...) this has to be in the User model as
Willem Renzema described:
class theModelClass {
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
And you'll call it like
$val = theModelClass::convertToMyNotation($anotherval);
If the function is handling user inputs (sanitizing he inputs,
checking the values, ...) then it has to go to the controller and
you'll use Hemc solution:
Create a public function in components/Controller.php
public function globalFunction(){
// do something here.
}
and access it anywhere by
$this->globalFunction();
If the function is an Helper: performing some actions that do not
depend on models or user inoput then you can create a new class that
you'll put in your component directory:
class core extends CComponent{
public static function convertToMyNotation($value){
//do whatever here
return $value;
}
}
And
$val = core::convertToMyNotation($anotherval);
Actually, I think you're looking for this answer instead:
http://www.yiiframework.com/wiki/31/use-shortcut-functions-to-reduce-typing/
In essence, in your entry script, before you load up Yii, include a global functions file:
require('path/to/globals.php');
Then, any function defined in that file can be used as a shortcut. Be careful, but enjoy the power! :-)
Create something like
Class Core extends CApplicationComponent{
public function doSomething(){}
}
and in config main.php
'components'=>array(
'core'=>array(
'class' => 'Core'
),
),
and now you can call whenever you want
Yii::app()->core->doSomething();
I have an application with several graphs and tables on it.
I worked fast and just made classes like Graph and Table that each contained a request object (pseudo-code):
class Graph {
private request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
public function refresh() {
request.getData(function() {
//refresh the display
});
}
}
Upon a GUI event (say, someone changes the date range dropdown), I'd just call the setters on the Graph instance and then refresh it. Well, when I added other GUI elements like tables and whatnot, they all basically had similar methods (setDateRange and other things common to the request).
What are some more elegant OOP ways of doing this?
The application is very simple and I don't want to over-architect it, but I also don't want to have a bunch of classes with basically the same methods that are just routing to a request object. I also don't want to set up each GUI class as inheriting from the request class, but I'm open to any ideas really.
As you commented the methods are identical. In that case I would suggest the following approach.
abstract class AbstractGUIElement {
protected request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
abstract function refresh();
}
Refreshing would probably be element specific so I have added it as an abstract method the inheriting types have to implement.
class Graph extends AbstractGUIElement {
public function refresh() {
// Graph specific refreshing
}
}
Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.