I'm trying to write a code where I could run a function based on it's previous variables. I don't know how to explain any better but a sample will do. I'm trying to do something like this:
<?php
$agric = new Agriculture;
$newplant = $agric-> setClass('plant');
$newanimal = $agric->setClass('animal');
$agric->getAll(); // returns null
$newplant->setProperties($plant1_data); //uses plant
$newanimal->setProperties($animal1_data); // uses animal
$newplant->setProperties($plant2_data); //uses plant
$newanimal->setProperties($animal2_data); // uses animal
$newplant->getAll(); // returns all plants array
$newanimal->getAll(); // returns all animals array
$agric->getAll(); // returns both plants array and animals
?>
So, In one form, the new variables calls the setClass in order to work and everytime it is is called they use their setClass method to know which type of argument they should use to run their code. I know I could do this differently but I seem to love this approach. Any help will do. Thanks in advance
Thanks #everyone.. I finally got the answer to that. So the reason why I'm giving it to #Dragos is because phone factory seems to be the best approach to it. So, all I did was to create two classes. One is Agriculture which has a method to get the properties of what was called using "getAll()" and the Other is HandleAgric which also has "getAll()". The HandleAgric has its own setClass which is static and every time it is called, it instantiates a new Agriculture with its default parameter. So, Both Classes will have setClass() and getAll() as method. Something like this
<?php
class HandleAgric{
private static $Agriculture;
public function __construct(){
self::setClass();
}
function __call($method,$args){
$self = new self;
if(method_exists($self::$Agriculture, $method)){
$call = call_user_func(array($self::$Agriculture, $method));
return $call;
}
return trigger_error("Error Found!!! Aborting");
}
public static function setClass($class=null){
self::$Agriculture = new Agriculture;
$call = self::$Agriculture->setClass($class);
return $call;
}
}
//Example of usage
$Agric = new HandleAgric();
$Plant = $Agric::setClass("plant");
$Animal = $Agric::setClass("animal");
$Plant->setProperties($arrayList);
$Animal->setProperties($arrayList);
# $Plant->getAll() //return plants properties in array;
# $Animal->getAll() //return Animal properties in array;
# $Agric->getAll() //return Agric (both plant and animal) properties in array;
?>
I believe this to be much better.
For your example to work, the method setClass from Agriculture must work like a factory: it instantiates a class based on the parameter and returns the object.
Agriculture class must also keep all the objects instantiated inside setClass method in its own internal array, so when getAll method is called, it iterates through each object and executes their own getAll methods.
As I am required to pirnt the ratings of products in JSON format, i have made a module with controller and rating.php file in model folder. We I run the controller it shows all the data from that table, But I required only a single row. So through the url i am passing a parameter, but it wont works. I am attaching my indexcontroller.php here. suggest me upon this.
<?php
class Modulename_CustomRating_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction ()
{
$arrParams = $this->getRequest()->getParams();
var_dump($arrParams);
$collection = Mage::getModel('Modulename_CustomRating_Model_CustomRating')->getCollection();
}
print_r (json_encode($collection ->getData()));
}
}
?>
As I am passing url as:localhost/magento/customrating?vote_id=1 , it is taking the parameter to it but returns whole table's data. I know this is due to getData(); but how to make to get the required row?
You have to use setEntityPkFilter method. Check Mage_Rating_Model_Resource_Rating_Option_Vote_Collection class for other methods.
$product_id = $this->getRequest()->getParam('product_id'); // or 'vote_id'
$collection = Mage::getModel('Modulename_CustomRating_Model_CustomRating')
->getResourceCollection()
->setEntityPkFilter($product_id);
If you want only 1 column you can try some Zend stuff because you can't use addAttributeToSelect. getSelect() returns Zend like query:
$adapter = $this->getConnection(); // $this->getConnection();
$collection->getSelect()
->reset(Zend_Db_Select::COLUMNS) // remove all columns
->columns('attribute_name'); // add only needed one
$result = $adapter->fetchAll($select);
Not sure whether this would work. It's not tested, just an idea.
i need differents results from a model but i don't understand if it is correct make a single call and leave to model all the work or make more calls and collect the result to pass to the view when tables aren't joined or when i need fetch one row from a table and differents rows from others.
First example (more calls, collect and send to view):
CONTROLLER
// call functions of model
$modelName = new Application_Model_DbTable_ModelName();
$rs1 = $modelName->getTest($var);
$rs2 = $modelName->getTest2($var2);
// collect data
$pippo = $rs1->pippo;
if ($rs2->pluto == 'test') {
$pluto = 'ok';
} else {
$pluto = 'ko';
}
// send to view
$this->view->pippo = $pippo;
$this->view->pluto = $pluto;
MODEL
public function getTest($var) {
...
select from db...
return $result;
...
}
public function getTest2($var) {
...
select from db...
return $result;
...
}
Second example (one call, model collect all data, return to controller and send to view):
CONTROLLER
// call one function of model
$modelName = new Application_Model_DbTable_ModelName();
$rs = $modelName->getTest($var);
MODEL
public function getTest($var) {
...
select from db...
if ($result > 0) {
call other function
call other function
collect data
return $result;
...
}
Thanks
There's no one correct answer to this question, but in general, you should endeavor to keep your business logic in one place. Think of it as, "thin controller, thick model." I.e., keep the controllers as small and simple as possible and put all the business logic in the models.
There seems to be a few questions here:
But if i don't need to interact with db and i need only a simply
function is better put that function in model? For example:
CONTROLLER:
public function printAction() {
$data = $this->getRequest()->getPost();
$label = "blablabla";
$this->view->label = $label;
}
first, in the context of Zend Framework this particular example doesn't make much sense. The whole point of the controller is to populate the view template. However, I do get the idea. I would point you to Action Helpers and View helpers as a means to address your concerns. You can always add a utility class to your library for those pieces of code that don't seem to fit anywhere else.
Action Helpers typically are employed to encapsulate controller code that may be repetitive or reusable. They can be as simple or as complex as required, here is a simple example:
class Controller_Action_Helper_Login extends Zend_Controller_Action_Helper_Abstract
{
/**
* #return \Application_Form_Login
*/
public function direct()
{
$form = new Application_Form_Login();
$form->setAction('/index/login');
return $form;
}
}
//add the helper path to the stack in the application.ini
resources.frontController.actionhelperpaths.Controller_Action_Helper = APPLICATION_PATH "/../library/Controller/Action/Helper"
//the helper is called in the controller
$this->_helper->login();
a View helper does the same thing for the view templates:
class Zend_View_Helper_PadId extends Zend_View_Helper_Abstract
{
/**
* add leading zeros to value
* #param type $id
* #return string
*/
public function padId($id)
{
return str_pad($id, 5, 0, STR_PAD_LEFT);
}
}
//in this example the helper path is added to the stack from the boostrap.php
protected function _initView()
{
//Initialize view
$view = new Zend_View();
//add custom view helper path
$view->addHelperPath('/../library/View/Helper');
//truncated for brevity
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer');
$viewRenderer->setView($view);
//Return it, so that it can be stored by the bootstrap
return $view;
}
//and to use the helper in the view template
//any.phtml
<?php echo $this->padId($this->id) ?>
i need differents results from a model but i don't understand if it is
correct make a single call and leave to model all the work or make
more calls and collect the result to pass to the view when tables
aren't joined or when i need fetch one row from a table and differents
rows from others.
This question is more about structure then about correctness.
You can interact with your database table models in Action and View helpers for simple/repetitive queries if you need to, however most developers might frown on this approach as being difficult to maintain or just ugly.
Many people seem to favor Doctrine or Propel to help them manage their database needs.
At this point I like to roll my own and currently favor domain models and data mappers, not an end all be all pattern, but seems to be appropriate to your question.
This is not a simple suggestion to implement for the first time, however i found two articles helpful to get started:
http://phpmaster.com/building-a-domain-model/
http://phpmaster.com/integrating-the-data-mappers/
and if you really want to get into it try:
http://survivethedeepend.com/
I hope this answers at least a part of your questions.
So basically I'm making a leap from procedural coding to OOP.
I'm trying to implement the principles of OOP but I have a nagging feeling I'm actually just writing procedural style with Objects.
So say I have a list of pipes/chairs/printers/whatever, they are all all listed as products in my single table database. I need to build a webapp that displays the whole list and items depending on their type, emphasis is on 'correct' use of OOP and its paradigm.
Is there anything wrong about just doing it like:
CLass Show
{
public function showALL(){
$prep = "SELECT * FROM myProducts";
$q = $this->db-> prepare($prep);
$q->execute();
while ($row = $q->fetch())
{
echo "bla bla bla some arranged display".$row['something']
}
}
and then simply
$sth = new show();
$sth->showAll();
I would also implement more specific display methods like:
showSpecificProduct($id)->($id would be passed trough $_GET when user say clicks on one of the links and we would have seperate product.php file that would basically just contain
include('show.class.php');
$sth = new show();
$sth->showSpecificProduct($id);
showSpecificProduct() would be doing both select query and outputing html for display.
So to cut it short, am I going about it allright or I'm just doing procedural coding with classes and objects. Also any ideas/hints etc. on resolving it if I'm doing it wrong?
As well as the model practices described by #Phil and #Drew, I would urge you to separate your business, data and view layers.
I've included a very simple version which will need to be expanded upon in your implementation, but the idea is to keep your Db selects separate from your output and almost "joining" the two together in the controller.
class ProductController
{
public $view;
public function __construct() {
$this->view = new View;
}
public function indexAction() {
$model = new DbProductRepository;
$products = $model->fetchAll();
$this->view->products = $products;
$this->view->render('index', 'product');
}
}
class View
{
protected $_variables = array();
public function __get($name) {
return isset($this->_variables['get']) ? $this->_variables['get'] : null;
}
public function __set($name, $value) {
$this->_variables[$name] = $value;
}
public function render($action, $controller) {
require_once '/path/to/views/' . $controller . '/' . $action . '.php';
}
}
// in /path/to/views/product/index.php
foreach ($this->products as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
A better fit would be to implement a repository pattern. An example interface might be
interface ProductRepository
{
public function find($id);
public function fetchAll();
}
You would then create a concrete implementation of this interface
class DbProductRepository implements ProductRepsoitory
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
// prepare execute SQL statement
// Fetch result
// return result
}
public function fetchAll()
{
// etc
}
}
It's generally a bad idea to echo directly from a method or function. Have your methods return the appropriate objects / arrays / whatever and consume those results.
The scenario you are describing above seems like a good candidate for MVC.
In your case, I would create a class strictly for accessing the data (doing selects of product categories or specific products) and then have a different file (your view) take the output and display it.
It could look something like this:
class Product_Model {
public function find($prodId) { ... }
public function fetchAll($category = '') { ... }
public function search($string) { ... }
}
Then somewhere else you can do:
$products = new Product_Model();
$list = $products->fetchAll(37); // get all from category 37
// in true MVC, you would have a view that you would assign the list to
// $view->list = $list;
foreach($ilst as $product) {
echo "Product ID {$product['id']} - {$product['name']} - {$product['cost']}<br />\n";
}
The basic principle of MVC is that you have model classes that are simply objects representing data from some data source (e.g. database). You might have a mapper that maps data from the database to and from your data objects. The controller would then fetch the data from your model classes, and send the information to the view, where the actual presentation is handled. Having view logic (html/javascript) in controllers is not desirable, and interacting directly with your data from the controller is the same.
first, you will want to look into class autoloading. This way you do not have to include each class you use, you just use it and the autoloader will find the right file to include for you.
http://php.net/manual/en/language.oop5.autoload.php
each class should have a single responsibility. you wouldn't have a single class that connects to the database, and changes some user data. instead you would have a database class that you would pass into the user class, and the user class would use the database class to access the database. each function should also have a single responsibility. you should never have an urge to put an "and" in a function name.
You wouldn't want one object to be aware of the properties of another object. this would cause making changes in one class to force you to make changes in another and it eventually gets difficult to make changes. properties should be for internal use by the object.
before you start writing a class, you should first think about how you would want to be able to use it (see test driven development). How would you want the code to look while using it?
$user = new User($db_object);
$user->load($id);
$user->setName($new_name);
$user->save();
Now that you know how you want to be able to use it, it's much easier to code it the right way.
research agile principles when you get a chance.
One rule of thumb is that class names should usually be nouns, because OOP is about having software objects that correspond to real conceptual objects. Class member functions are usually the verbs, that is, the actions you can do with an object.
In your example, show is a strange class name. A more typical way to do it would be to have a class called something like ProductViewer with a member function called show() or list(). Also, you could use subclasses as a way to get specialized capabilities such as custom views for particular product types.
I have this library in PHP non-Cake format, the usual PHP scripting which currently works like a charm. I need to use this in a Cake framework. The library file is as follow: (example extracted)
<?php
// REST API functions
function sendAction($itemurl, $itemimageurl, $sessionid, $userid, $rating=""){
global $someapiwebsiteURL, $apiKey, $tenantId;
$somewebsiteAPI = $someapiwebsiteURL.$action."?apikey=".$apiKey.
.....
................
}
//Codes extract
?>
I've come across a few ways of doing it. Currently confused, how am I going to place this library file into my Cake framework?
App::import()
Datasource
The functions in the library file above (I supposed it'd be used in one of my Controllers to render the data outputting through the view).
Currently working in a non-Cake framework structure, the view page is such as: (example extracted)
<?php
// my view page
$viewResponse = sendAction($itemdescription ,$itemurl , $itemimageurl,$sessionid,$userid);
//sample code only
?>
Both the files are working fine. The logic of putting it in a CakePHP framework is the problem here. Anyone may suggest "the" way of doing this without over-strenuously working on a data source? If we have to use a data source in App/models/datasources/, how exactly is the structure of it? Like, e.g., in datasource file, do we include the library functions? or is it some generic ReST datasource file which can be found here: CakePHP ReST datasource . I've gone through the cookbook chapter on datasource and understand we have to define the datasource in our database.php, but if someone is certain about their way of accomplishing it either using datasource or app::import() method, please share with more details?
UPDATE:
Hi Lionel!, thanks for filling up. Well, actually users will click on view action: function view (){} in my foods_controller. I'm appending some scripts here to include my view function in my foods_controller so maybe it may help you to help out easier. Thanks..
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid food', true));
$this->redirect(array('action' => 'index'));
}
$this->set('food', $this->Food->read(null, $id));
}
The view action triggers the send_action function, (each time a user clicks on view page on foods controller). So each time, a user clicks on view action, his (dynamic variables): userid, sessionid, that page's itemid, url, itemdescription; (timerange value is a static string value "ALL"), and if any (etc.), so far only these values are available: Will be used as the "parameters" in the Send Action function. What you wrote is close to what the codes can do. You're right. Except we should include the Send Action function inside the view() in foods controller?
If we look at dynamically filling in the variables mentioned in the point above, could you modify your second code (the code from your product_controller, e.g.) so it also works to receive the variables dynamically? (as you asked in the last update: how to get the parameters..)
Just to make it clear.
A user views the page. The send action collects data and send to the API. (as we've already done by calling the function in the library the (ACME.php). *just waiting for your update if possible, thanks.
In the function view() of the foods controller: there's also an additional calling. The (2)second calling which is this:
$recommendResponse = getRecommendations("otherusersviewed", $itemId, $userId);
The second calling calls the ACME.php library file in which there consists the (2)second function that retrieves data, here it is: (it's in working order, but just needs to be changed into a public static function like you did for the (1)first function. Could you help to modify this code too, please?:
function getRecommendations($recommendationType, $itemId, $userId){
// sample code similar to the first one.
}
That's all to it. It seems quite simple in the normal PHP format, and it works easily, but getting it on an MVC framweork is a bit challenging for some, a lot for me. Thanks for helping out, Lionel. :-)
P.S. Hi Lionel, I notice something missing in the library after changes? Look originally we have this:
$somewebsiteAPI = $someapiwebsiteURL.$action."?apikey=".$apiKey.
Look, the variables for $SomeWebsiteAPI and $SomeApiWebsiteURL are different. Did I miss out something? or you have modified so it is more efficient ? I see that the variable named $SomeWebsiteAPI is modified to become variable called $link ? and variable $SomeApiWebsiteURL is changed to the named variable, $url, am I right ? .. thanks.
Thanks, best regards. John Maxim
To me, if I have this piece of code, I would first wrap it into a static (or normal) class, and named it ACME, then I will move the acme.php into /apps/libs/acme.php. Then in the controller, I will use App::import('Lib', 'acme'). This action do nothing but just requiring the file, so you can just use it instantly by calling ACME::sendAction(...).
And regarding the global thing, you might just need to declare a static (or normal) class, then define the shared variables as part of the class properties, so you can share them among all the functions in the class.
For example, this is the /app/libs/acme.php
class ACME {
private static $someapiwebsiteURL = "http://thewebsite/api/1.0/";
private static $apiKey = "0010KIUMLA0PLQA665JJ";
private static $tenantId = "THE_TENANT_NAME";
/**
* Simple builder to build links from array of $params
*
* #param string $url The api url
* #param array $params The given parameters
* #return string built url
*/
private static function BuildLink($url="", $params=array()) {
$link = $url;
foreach($params as $k=>$v) {
$link .= "&$k=$v";
}
//Replace the first & to ?
$link = preg_replace("/&/", "?", $link, 1);
//Not sure if we need URL encode here, please uncomment this
//if the API could not work.
//$link = urlencode($link);
return $link;
}
public static function SendAction($action, $itemId, $itemdescription, $itemurl, $itemimageurl, $sessionid, $userid, $rating="") {
$somewebsiteAPI = self::BuildLink(self::$someapiwebsiteURL.$action, array(
"apikey"=>self::$apiKey,
"sessionid"=>$sessionid,
"userid"=>$userid,
"tenantid"=>self::$tenantId,
"itemid"=>$itemId,
"itemdescription"=>$itemdescription,
"itemurl"=>$itemurl,
"itemimageurl"=>$itemimageurl,
/**
* Assuming your API smart enough to only use this value when
* the action is "rate"
*/
"ratingvalue"=>$rating
));
$xml = simplexml_load_file($somewebsiteAPI);
return $xml;
}
public static function GetRecommendations($recommendationType, $itemId, $userId) {
$somewebsiteAPI = self::BuildLink(self::$someapiwebsiteURL.$recommendationType, array(
'apikey'=>self::$apiKey,
'tenantid'=>self::$tenantId,
'itemid'=>$itemId,
'userid'=>$userId
));
$xml = simplexml_load_file($somewebsiteAPI);
return $xml;
}
}
And in your controller
App::import('Lib', 'acme');
class FoodController extends AppController {
//Food is plural already I assume? You can just use
//food, should be ok I think, else it will be weird
//to use /foods/view/?
var $name = "Food";
var $uses = array("Item", "Food");
function view($id="") {
//We accepts only valid $id and $id > 0.
//Take notes that this $id will be a string, not int.
if (ctype_digit($id) && $id > 0) {
//I don't know how you would gather the information, but I assume you
//have a database with the information ready.
//I assumed you have an `items` table
$item = $this->Item->findById($id);
$sessionid = "00988PPLO899223NHQQFA069F5434DB7EC2E34"; //$this->Session->...?
$timeRange = "ALL";
$userid = "24EH1725550099LLAOP3"; //$this->Auth->user('id')?
if (!empty($item)) {
$desc = $item['Item']['description'];
$url = "/foods/view/".$id;
$img = $item['Item']['img'];
$viewResponse = ACME::SendAction("view", $id, $desc ,$url, $img, $sessionid, $userid);
$this->set('food', $this->Food->read(null, $id));
}else{
$this->Session->setFlash(__('Invalid food', true));
$this->redirect(array('action' => 'index'));
}
}else{
$this->Session->setFlash(__('Invalid food', true));
$this->redirect(array('action' => 'index'));
}
}
}
Edit
The code has been filled up, and of course, without any warranty :). I personally don't really like to have long arguments in a function (like SendAction, error prune), rather use shorter one like the $params in ACME::BuildLink. But just to respect your code, I didn't modify much on the SendAction method.
Then I'm not too sure how you would make use of this code, so I assumed you have a ProductsController, and somehow the user trigger url like /products/send_action/. If you can provide more information, then we would be able to help out.
Edit Again
I have modified the ACME class, as well as the controller. Yea I do miss out some variables, but I had added them back to the updated code.
Not too sure if it would work (perhaps typo), you can just modify the code if it doesn't work for you.
And for personal conventions, I usually capitalize methods which are static, like ACME:GetRecommendations or ACME::SendAction.
Oh yea, I better stick back to the variables you used. Sorry for modifying them, just I don't like long names :)
And btw, the RoadRunner's ACME Corporation? Lol!
Cheers
Lionel