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.
Related
I'm currently learning the ropes of the MVC pattern and came across a problem I can't seem
to fix in a way I want and is in line with the MVC pattern.
I have set up the router, controllers and views up successfully.
The only thing I don't really get is the use of the Model. I know it's supposed to
serve the Data to the view, and here it is I have a problem.
I want to pass a function thru my view method, but it executes before it should be.
is there a way
I will try to be as specific as possible about the situation so sorry for the long post.
The controller class is this:
class Controller{
private $tpl_name = 'default';
public function model($model){
require('../admin/model/'.$model.'.model.php');
return new $model();
}
public function view($page_title,$file_paths,$params,$data = []) {
// takes an array with the file paths
$this->content = $file_paths;
$tpl_name = $this->tpl_name;
require_once('templates/'.$tpl_name.'/header.php');
require_once('templates/'.$tpl_name.'/nav.php');
require_once('templates/'.$tpl_name.'/content-top.php');
foreach ($file_paths as $content){
require_once('view/'.$content);
}
require_once('templates/'.$tpl_name.'/content-bottom.php');
require_once('templates/'.$tpl_name.'/footer.php');
}
}
The view renders the template I want, takes parameters from the router and, the data that
needs to be handled in the desired view. So far so good.
I want to serve my posts in my admin panel that displays a table of all the posts in the DB.
I have written a method that fetches the data, and a method that writes the data.
class Post{
......
//other functions above
public function displayPosts(){
// get's all the posts form the data base, returns an object array
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
}
static public function write(Array $posts){
foreach($posts as $single){
// for each object in the array, assign the vars so the view can handle them
// to create a single row in the table for each object:
$trashed = $single->getTrashed();
$id = $single->getID();
$title = $single->getTitle();
$category = $single->getCategory();
$content = $single->getContent();
$author = $single->getAuthor();
$date = $single->getDate();
$approved = $single->getApproved();
$dbt = $single->getDbt();
// This is a template which represents a table row with the post data I need.
require('view/content_table.php');
}
//controller file (needs to moved to other file later): handles approve/remove/edit/delete actions.
require('view/manage_content.php');
}
}
Now we have arrived at the problem:
When I call the model in my controller and render the view, it will execute immediatly
before the rest of my view loads, resulting in errors, although it displays the data,
it is not in my template, but above it, just in plain text.
errors:
Notice: Undefined variable: _SESSION in /Volumes/HDD Mac/Websites/server/admin/view/content_table.php on line 8
Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at ...)
class Dashboard extends Controller {
public function index($params = null){
$model = $this->model('Post');
$posts = $model->displayPosts();
// view takes: page_title,[array of view files],params from the router,array of data from model
$this->view('Dashboard',['admin.php'],$params,[ 'posts' => $posts]);
}
}
Before I was trying to use MVC I just outputted this in my view:
And it worked just fine.
Non relevant HTML above
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
Non relevant HTML below
But now when I pass the display post function, I just want to do this in my view:
echo $data['posts'];
which doesn't work because it already executed my Write function.
The only way I could work around like this was by adding the content of my write function to the view,
and only pass the fetchContent method to my view method (this will output an array of objects).
But since I need this info in two place I dont want to repeat this code, I would prefer echoing
all out.
Non relevant HTML above
$posts = $data['posts'];
foreach($posts as $single){
// for each object in the array, assign the vars so the view can handle them
// to create a single row in the table for each object:
$trashed = $single->getTrashed();
$id = $single->getID();
$title = $single->getTitle();
$category = $single->getCategory();
$content = $single->getContent();
$author = $single->getAuthor();
$date = $single->getDate();
$approved = $single->getApproved();
$dbt = $single->getDbt();
// This is a template which represents a table row with the post data I need.
require('view/content_table.php');
}
//controller file (needs to moved to other file later): handles approve/remove/edit/delete actions.
require('view/manage_content.php');
Non relevant HTML below
Is it bad practise to just skip the use of the Model here and do it like this:
Non relevant HTML above
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
Non relevant HTML below
Or is there a way to rewrite my Post::Write function? Or just use the foreach loop in the view?
Thank you all for taking the time!
If you need more info, just ask:-)
I have create module for testing in Magento and i call change action url from browser and there SQL query for catalog/product is printed.
I user this code
<?php
class Test_Demo_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction()
{
$this->loadLayout();
$block = $this->getLayout()->createBlock('capacityweb/Test','Test',array('template' => 'capacity/web/test.phtml'));
$this->getLayout()->getBlock('content')->append($block);
$this->renderLayout();
}
public function changeAction()
{
$this->loadLayout();
$this->renderLayout();
$action=$this->getRequest()->getParam('action');
$id=$this->getRequest()->getParam('id');
if($action!=null && $id!=null)
{
$relContact = Mage::getModel('catalog/product')->getCollection()->load($id);
}
}
}
if i use
$relContact = Mage::getModel('catalog/product')->getCollection()->load($id);
then magento display SQL query but instead of this
i use
$relContact = Mage::getModel('catalog/product')->getCollection();
then magento not display anything.
So how to fix issue with
$relContact = Mage::getModel('catalog/product')->getCollection()->load($id);
After reading your code, I think what you are trying to do is load one product, in this case you have the $id.
In this case you should use:
$product = Mage::getModel('catalog/product')->load($id)
And then you can access the desire data through:
$product->getName();
$product->getDescription()
... and so on.
Greetings
This is not an issue.
Magento uses the Lazy Loading design pattern for collections.
The sql for a collection is not executed until the collection is loaded or until you iterate through the results.
So Mage::getModel('catalog/product')->getCollection() will return an object that is an instance of Mage_Catalog_Model_Resource_Product_Collection that you can use how you need, but you don't have the products yet.
When calling Mage::getModel('catalog/product')->getCollection()->load() (by the way...the $id parameter is useless), then the query SELECT FROM catalog_product_entity..... is exectuted and in $collection->getIterator() you will have the records from the database as instances of Mage_Catalog_Model_Product.
[EDIT]
If you want a single product skip the getCollection call. You can get the instance of a product like this
$product = Mage::getModel('catalog/product')->load($id);
//changes some attributes
$product->setData('description', $description);
//or
//$product->setDescription($description);
$product->save().
See more details about it here.
So this question comes from another i created you can find here But i have changed my code so now it looks something like this:
Car Controller
public function indexAction(){
$category = new Application_Model_CarMapper();
$gcat = $this->getRequest()->getPathInfo();
//get id
$id = $category->find_id_by_name($gcat);
$this->view->title = $category->get_sub_cat_select($id);
}
This is a new function i created inside the mapper:
public function find_id_by_name($name){
$select = $this->getDbTable()->query("SELECT * FROM car_category WHERE category_name = '{$name}'");
$result = $select->fetchAll();
if(!$result) {
return;
}
return $result[0]['ID'];
}
I am testing it out by the title but it just doesnt seem to display at all. I would like it to display the drop down menus for the specific category, e.g
car-live.local/cars/Volvo ---> "Welcome to the Volvo Car Finder"
car-live.local/cars/BMW ---> "Welcome to the BMW Car Finder"
I know it is not working as i have to split down the URL even more, as right now it is finding the id via the URL, but i am unsure how to do this :s Any light you can shed on this would be extremely grateful.. Thanks.
[EDIT]
New code:
public function indexAction(){
$category = new Application_Model_CarMapper();
$gcat = explode("/", $this->getRequest()->getPathInfo());
if(isset($gcat['category_name'])) {
$id = $category->find_id_by_name($gcat);
$this->view->title = $category->get_sub_cat_select($id);
}
}
I'm not sure if I understood correctly your issue, but from code it seems like you're having issues dealing with the URL. So, let's take this example:
car-live.local/car/Volvo
After you get at indexAction you should explode by the /.
This is return an array with all the components of the URL, then you take the last one in this case would be Volvo and send it to your find_id_by_name() method.
My usual approach to this problems is to follow something like:
Explode by slashes;
Get the first one (in this case car and "route" it to a cars class, by sending to the constructor that and the rest of the request;
The car class constructor then takes the next part of the URL Volvo and "routes" it an appropriate method like getVolvo() or whatever you need...
You can route it to what you need, but just try to delegate the responsibilities of processing the URL to the appropriate classes. Example:
/cars/whatEverCar => Should be processed by class car
/bike/whatEverBike => Should be processed by class bike
Hope this helps.
Edit:
Your find_id_by_name() method can have some URL like:
/car/find_id_by_name/Volvo
Am I explaining this well? You can take parts of the URL and call methods directly... and send the rest of the URL.
Edit 2: Code example...
The following code is a very nice solution for your problem:
<?php
// Call this class as soon the site stars
class Router {
public function __construct() {
// In some really cool way get your full URL, for example I'm gonna use a string...
$fullURL = "example.com/Car/findCarById/Volvo";
array_shift($fullURL); // removes "example.com", obvious stuff...
$explodedUrl = explode("/", $fullURL);
$targetClass = $explodedUrl[0]; // Temporary store the target is "Car"
array_shift($fullURL); // Removes "Car", we don't need it anymore...
$target = new $targetClass($fullURL); // call the Car class responsible for handling all the "Car" related stuff!
}
}
class Car {
public function __construct($request) {
// $request is an array with "findCarById", "Volvo"
$targetMethod = $request[0]; // Temporary store the target is "findCarById"
array_shift($request);
$this->$targetMethod($request);
}
private function findCarById($parameter) {
// $parameter is an array with "Volvo"..
// Now to your query and other cool stuff with $parameter[0];
print "I'm the method findCarById() of the class Car called by the main router to find the ID for {$parameter}...";
}
}
?>
If you need to add more functions to your Car class, just add another method and then call it over the URL with his name, same way I did with findCarById().
Warning: This code might have minor bugs, it was written on Notepad and IS NOT tested.
This should return a list of about five locations. It returns nothing with no errors. I've tested the sql using mysql workbench. It returns the data just fine. Right now I'm writing the back end so I'm not concerned with using views or the dataprovider. I'm just making sure my back end functions work. So with that in mind, how would you return the data retrieved by findAllBySql?
class CashLogic
{
public function AllLocations()
{
$model = new Locations;
$allLocations = $model->findAllBySql("SELECT name from locations");
return $allLocations;
}
}
class SiteController extends Controller
{
public function actionIndex()
{
$model = new CashLogic;
$data = $model->AllLocations();
return $data;
}
}
The findAllBySql() method returns an array of models. From your code it seems you only want the names of locations. An alternative method is
$AllLocations=CHtml::listData(Locations::model()->findAll(),'name','name');
This will return an array of the form array('name'=>'name','name'=>'name'). A better solution would be to replace the first name with the primary key of your locations table.
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.