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');
}
}
Related
So I am trying to implement a friendlist, the above is the SQL diagram I made for my simple project and after generating the Models. I realized there was something wrong with the way Gii generated the model.
I wanted to make a many-to-many relationship with User to itself, but this is what I got:
class User {
...
public function getPosts()
{
return $this->hasMany(Post::className(), ['userId' => 'id']);
}
}
class Friend {
...
public function getFriend()
{
return $this->hasOne(Member::className(), ['id' => 'friendId']);
}
}
The User class doesn't have any relationship with itself, I expected something like getUsers() inside of User, but it didn't generate it. I initially thought about not making a model with the junction table, but I did so just to see what would happen. I don't think I need it. So I am not sure how to do this correctly? Do I need to get rid of my Junction Table Models and Do I need to make the relationship between User to itself and User to Message manually? I thought about doing a many-to-many in User and Message and a many-to-many in User for User. Is this the right thing? Tell me if I am wrong. Thank you.
You are on a true way. You need a junction table for implementing your goal. Easily as you done this, you must define two model: User and Friend. Now on your User model at first you must define a relation for get the list of all friends, Suppose call it getFriendsLists:
public function getFriendsLists()
{
return $this->hasMany(Friend::className(), ['userId' => 'id']);
}
This relation says that "Get me all account that are connected with me, i.e. if my id is 102, this relation return all record of friend table that their userIds are 102". Well, now we get all friends with a relation on User model, let call him getFriends:
public function getFriends()
{
return $this->hasMany(User::className(), ['friendId' => 'id']
->via('friendsList');
}
Notice that 'friendsList' as is a parameter of via method, is our predefined relation on top of this answer. Now easily you can get all account that are friends of our example (User with id 102):
public FriendController extends Controller
{
// Some code goes here!
public function actionFriendList($id)
{
$user = User::findOne($id);
$friends = $user->friends;
return $this->render('friend-list', ['friendsArray' => $friends]);
}
}
And use them on your friend-list view file as $friendsArray variable. Extra note that $user->friends use friends relation that you defined on User model with getFriends method.
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);
I'm not sure I am using the proper terminology, so I will describe what I want to achieve.
I have a controller called ControllerA and want a "virtual" controller called ControllerB to function exactly the same as ControllerA.
Basically I just want the url site.com/ControllerB to load up the same page as site.com/ControllerA (but not redirect).
Hope my description is clear enough.
You can achieve what you want with a simple URL rule:
'controllerA/<a>' => 'controllerA/<a>',
'controllerB/<a>' => 'controllerA/<a>',
Read more about URL rules here: http://www.yiiframework.com/doc/guide/1.1/en/topics.url#user-friendly-urls
You can extend ControllerA with ControllerB and provide extended controller name. Next override getViewPath method. Attribute extendedControler give us basic controller name.
class ControllerBController extends ControllerAController
{
private $extendedControler = 'ControllerA';
public function getViewPath() {
$nI = Yii::app()->createController($this->extendedControler);
return $nI[0]->getViewPath();
}
}
Of course you can use some string modification. Like str_ireplace:
class Klient2Controller extends KlientController
{
public function getViewPath() {
//We must extract parent class views directory
$c = get_parent_class($this);
$c = str_ireplace('Controller', '', $c); //Extract only controller name
$nI = Yii::app()->createController($c);
return $nI[0]->getViewPath();
}
}
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'm trying to follow the Law Of Demeter ( see http://en.wikipedia.org/wiki/Law_of_Demeter , http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/ ) as I can see the benefits, however I've become a little stuck when it comes to domain objects.
Domain objects do naturally have a chain and sometimes it's necessary to display the information about the entire chain.
For instance, a shopping basket:
Each order contains a user, delivery info and a list of items
Each order item contains a product and quantity
Each product has a name and price.
Each user contains a name and address
The code which displays the order information has to use all the information about the order, users and products.
Surely it's better and more reusable to get this information through the order object e.g. "order.user.address.city" than for some code higher up to do queries for all the objects I listed above then pass them into the code separately?
Any comments/suggestions/tips are welcome!
One problem with using chained references, such as order.user.address.city, is that higher-order dependencies get "baked into" the structure of code outside the class.
Ideally, in cases when you refactor your class, your "forced changes" should be limited to the methods of the class being refactored. When you have multiple chained references in the client code, refactoring drives you to make changes in other places of your code.
Consider an example: suppose that you'd like to replace User with an OrderPlacingParty, an abstraction encapsulating users, companies, and electronic agents that can place an order. This refactoring immediately presents multiple problems:
The User property will be called something else, and it will have a different type
The new property may not have an address that has city in cases when the order is placed by an electronic agent
The human User associated with the order (suppose that your system needs one for legal reasons) may be related to the order indirectly, - for example, by being a designated go-to person in the definition of the OrderPlacingParty.
A solution to these problems would be to pass the order presentation logic everything that it needs directly, rather than having it "understand" the structure of the objects passed in. This way you would be able to localize the changes to the code being refactored, without spreading the changes to other code that is potentially stable.
interface OrderPresenter {
void present(Order order, User user, Address address);
}
interface Address {
...
}
class PhysicalAddress implements Address {
public String getStreetNumber();
public String getCity();
public String getState();
public String getCountry();
}
class ElectronicAddress implements Address {
public URL getUrl();
}
interface OrderPlacingParty {
Address getAddress();
}
interface Order {
OrderPlacingParty getParty();
}
class User implements OrderPlacingParty {
}
class Company implements OrderPlacingParty {
public User getResponsibleUser();
}
class ElectronicAgent implements OrderPlacingParty {
public User getResponsibleUser();
}
I think, when chaining is used to access some property, it is done in two (or at least two) different situation. One is the case that you have mentioned, for example, in your presentation module, you have an Order object and you would like to just display the owner's/user's address, or details like city. In that case, I think it is of not much problem if you do so. Why? Because you are not performing any business logic on the accessed property, which can (potentially) cause tight coupling.
But, things are different if you use such chaining for the purpose of performing some logic on the accessed property. For example, if you have,
String city = order.user.address.city;
...
order.user.address.city = "New York";
This is problematic. Because, this logic is/should more appropriately be performed in a module closer to the target attribute - city. Like, in a place where the Address object is constructed in the first place, or if not that, at least when the User object is constructed (if say User is the entity and address the value type). But, if it goes farther than that, the farther it goes, the more illogical and problematic it becomes. Because there are too many intermediaries are involved between the source and the target.
Thus, according to the the Law of Demeter, if you are performing some logic on the "city" attribute in a class, say OrderAssmebler, which accesses the city attribute in a chain like order.user.address.city, then you should think of moving this logic to a place/module closer to the target.
You're correct and you'll most likely model your value objects something like this
class Order {
User user;
}
class User {
Address shippingAddress;
Address deliveryAddress;
}
class Address {
String city;
...
}
When you start considering how you will persist this data to a database (e.g. ORM) do you start thinking about performance. Think eager vs lazy loading trade offs.
Generally speaking I adhere to the Law of Demeter since it helps to keep changes in a reduced scope, so that a new requirement or a bug fix doesn't spread all over your system. There are other design guidelines that help in this direction, e.g. the ones listed in this article. Having said that, I consider the Law of Demeter (as well as Design Patterns and other similar stuff) as helpful design guidelines that have their trade-offs and that you can break them if you judge it is ok to do so. For example I generally don't test private methods, mainly because it creates fragile tests. However, in some very particular cases I did test an object private method because I considered it to be very important in my app, knowing that that particular test will be subject to changes if the implementation of the object changed. Of course in those cases you have to be extra careful and leave more documentation for other developers explaining why you are doing that. But, in the end, you have to use your good judgement :).
Now, back to the original question. As far as I understand your problem here is writing the (web?) GUI for an object that is the root of a graph of objects that can be accessed through message chains. For that case I would modularize the GUI in a similar way that you created your model, by assigning a view component for each object of your model. As a result you would have classes like OrderView, AddressView, etc that know how to create the HTML for their respective models. You can then compose those views to create your final layout, either by delegating the responsibility to them (e.g. the OrderView creates the AddressView) or by having a Mediator that takes care of composing them and linking them to your model. As an example of the first approach you could have something like this (I'll use PHP for the example, I don't know which language you are using):
class ShoppingBasket
{
protected $orders;
protected $id;
public function getOrders(){...}
public function getId(){...}
}
class Order
{
protected $user;
public function getUser(){...}
}
class User
{
protected $address;
public function getAddress(){...}
}
and then the views:
class ShoppingBasketView
{
protected $basket;
protected $orderViews;
public function __construct($basket)
{
$this->basket = $basket;
$this->orederViews = array();
foreach ($basket->getOrders() as $order)
{
$this->orederViews[] = new OrderView($order);
}
}
public function render()
{
$contents = $this->renderBasketDetails();
$contents .= $this->renderOrders();
return $contents;
}
protected function renderBasketDetails()
{
//Return the HTML representing the basket details
return '<H1>Shopping basket (id=' . $this->basket->getId() .')</H1>';
}
protected function renderOrders()
{
$contents = '<div id="orders">';
foreach ($this->orderViews as $orderView)
{
$contents .= orderViews->render();
}
$contents .= '</div>';
return $contents;
}
}
class OrderView
{
//The same basic pattern; store your domain model object
//and create the related sub-views
public function render()
{
$contents = $this->renderOrderDetails();
$contents .= $this->renderSubViews();
return $contents;
}
protected function renderOrderDetails()
{
//Return the HTML representing the order details
}
protected function renderOrders()
{
//Return the HTML representing the subviews by
//forwarding the render() message
}
}
and in your view.php you would do something like:
$basket = //Get the basket based on the session credentials
$view = new ShoppingBasketView($basket);
echo $view->render();
This approach is based on a component model, where the views are treated as composable components. In this schema you respect the object's boundaries and each view has a single responsibility.
Edit (Added based on the OP comment)
I'll assume that there is no way of organizing the views in subviews and that you need to render the basket id, order date and user name in a single line. As I said in the comment, for that case I would make sure that the "bad" access is performed in a single, well documented place, leaving the view unaware of this.
class MixedView
{
protected $basketId;
protected $orderDate;
protected $userName;
public function __construct($basketId, $orderDate, $userName)
{
//Set internal state
}
public function render()
{
return '<H2>' . $this->userName . "'s basket (" . $this->basketId . ")<H2> " .
'<p>Last order placed on: ' . $this->orderDate. '</p>';
}
}
class ViewBuilder
{
protected $basket;
public function __construct($basket)
{
$this->basket = $basket;
}
public function getView()
{
$basketId = $this->basket->getID();
$orderDate = $this->basket->getLastOrder()->getDate();
$userName = $this->basket->getUser()->getName();
return new MixedView($basketId, $orderDate, $userName);
}
}
If later on you rearrange your domain model and your ShoppingBasket class can't implement the getUser() message anymore then you will have to change a single point in your application, avoid having that change spread all over your system.
HTH
The Law Of Demeter is about calling methods, not accessing properties/fields. I know technically properties are methods, but logically they're meant to be data. So, your example of order.user.address.city seems fine to me.
This article is interesting further reading: http://haacked.com/archive/2009/07/13/law-of-demeter-dot-counting.aspx