How to make an action for missing actions? - php

I am very new to cakePHP.
I am working on a controller like so:
class DesignersController extends AppController
{
var $name = 'Designers';
function index()
{
$data = $this->Designer->find('all');
$this->set('designers', $data);
}
function belle_etoile()
{
$this->show_designer("belle etoile");
}
function ritani()
{
$this->show_designer("ritani");
}
function swarovski()
{
$this->show_designer("swarovski");
}
function verragio()
{
$this->show_designer("verragio");
}
private function show_designer($designer)
{
$this->layout = 'first';
$data = $this->Designer->find('first', array('conditions' => array('Designer.name' => $designer)));
$this->set('data', $data);
$this->render('show_designer');
}
}
As you can see many of the "actions" are shortcuts for show_designer/param action where param is the name of the shortcut action.
Every one of these actions is a "designer" in the database. I just don't want to have to make the url designers/show_designer/ritani, I would rather it just be designers/ritani.
This works, but the problem is:
I have to create a bunch of redundant functions for every designer, and if a new designer gets added, it won't work until I add a function for it.
I would rather have a function/action that runs if the action requested is missing, and has the action that was requested as a parameter
so if I request url designers/stardust, since stardust is not defined as an action it would call the catch_all action with stardust as the parameter.
So instead of a bunch of redundant functions I could just have this:
function catch_all($action)
{
$this->show_designer($action)
}
Is there anyway to do something like this?

Use routing instead
// add this to app/config/routes.php
Router::connect('/designer/*', array('controller' => 'designers', 'action' => 'designer'));
In your controller
// and remove all actions 'belle_etoile', 'swarovski' etc
// change `show_designer` to `public designer`
class DesignersController extends AppController {
var $name = 'Designers';
function designer($name)
{
$this->layout = 'first';
$data = $this->Designer->find('first', array('conditions' => array('Designer.name' => $name)));
if(!empty($data)) {
$this->set('data', $data);
$this->render('show_designer');
} else {
$this->redirect('index');
}
}
}

have you tried adding a call method:
function __call($action,$params = array())
{
$this->show_designer($action)
}
Im not 100% shore how cake calls its methods but it should work:
Example of the usage:
finale class Test
{
function __call($action,$params = array())
{
echo $action . " called:<br />";
foreach($params as $param)
{
echo "Param: "$param . "<br />";
}
}
}
$test = new Test();
$test->SomeNonExistantmethod("param 1","param 2");
This would output:
SomeNonExistantmethod called:
param: param 1
param: param 2
your class would be like so:
class DesignersController extends AppController
{
var $name = 'Designers';
var $allowed = array(
"belle_etoile",
"ritani",
"swarovski",
"verragio"
);
function index()
{
$data = $this->Designer->find('all');
$this->set('designers', $data);
}
function __call($action,$params = array())
{
if(in_array($action,$this->allowed))
{
$this->show_designer($action);
}
}
private function show_designer($designer)
{
$this->layout = 'first';
$data = $this->Designer->find('first', array('conditions' => array('Designer.name' => $designer)));
$this->set('data', $data);
$this->render('show_designer');
}
}

Related

Instantiating a class based on paramters in PHP

I have a slightly different problem where I'm instantiating a class from within another class which works when I manually invoke the method but doesn't when I automate it. The code is below:
class Button {
function __construct($params = Array()) {
// get some common properties
}
}
class HelperButton extends Button {
function __construct($params = Array()) {
parent::__construct($params);
}
function getHelperButton() {
//generate helper button code
}
}
I'm using the above class in the following classes
include_once('field.class.php');
include_once('buttons.class.php');
class Field {
function __construct() {
}
function setName($name){
$this->name = $name;
}
}
class TextField extends Field {
function __construct() {
parent::__construct();
}
function setFiller($fill = "") {
$helperbtn = new HelperButton($fill);
$this->helperbtn = $helperbtn->getHelperButton();
}
function getTextField(){
$this->textfield = "<input name='blah' />".$this->heperbtn;
return $this->textfield;
}
}
class Segment extends Field {
function __construct() {
parent::__construct();
}
function addTextField($params = array()) {
if(is_array($params)){
$txtfld = new TextField();
}
if (isset($params['type']['filler'])) {
$txtfld->setFiller($params['type']['fill']);
}
$this->segment .= $txtfld->getTextField();
}
function addFillerField($params = array()) {
$params['type']['filler'] = true;
$this->addTextField($params);
}
}
Now, I'm generating the text field in my page dynamically with params. The following code works well:
$segment = new Segment();
$segment->addFillerField("type"=>array("filler"=>true,"fill"=>"sometext")); //this should add a button next to the field which will open a window that will load the 'fill' text
$segment->render(); // this will print the segment output to screen
However, the following code does not work. It prints the text field alright but it does not print the button:
$fldmthds = array("FLRFLD" => "FillerField");
$pagedtl = array("FCLSID" => "FLRFLD", "FLDNAM" => "Field Name", "FLDTYP" => "filler:true,fill:sometext");
$mysegment = new Segment();
foreach ($pagedtl as $flds => $val) {
$mthd = "add" . $fldmthds[$val['FCLSID']]; // resolves to addFillerField
$params = array(
'name' => $val['FLDNAM'],
'type' => json_decode("{" . $val['FLDTYP'] . "}", true)
);
$mysegment->{$mthd}($params);
}
$mysegment->render();
PHP did not give any error/warning. It just goes silent and forgets the button. Does anyone see what is going wrong here? Thanks for your time.
Can't comment for now... just one thing: json_decode() will fail. To receive valid json change {filler:true,fill:sometext} to {"filler": true, "fill": "sometext"}

Modx: getCollection query is not working

Inside my processor class I have a statement that grabs all the projects from a db table and formats them to be displayed. This method does not work and halts at the getCollection call.
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public function initialize() {
return parent::initialize();
}
public function process() {
$result = $this->modx->getCollection('ManagerProjects');
$project_names = array();
foreach ($result as $row) {
$projects = unserialize($row->get('manager_projects'));
foreach($projects as $short_code => $project) {
$project_names[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
}
return '{"total":' . count($project_names) . ',"results":' . $this->modx->toJSON($project_names) . ',"success":true}';
}
...
}
This code that uses plain SQL does work:
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public function initialize() {
return parent::initialize();
}
public function process() {
$leadersql = "SELECT * FROM `modx_manager_projects`";
$query = $this->modx->query($leadersql);
$project_names = array();
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$projects = unserialize($row['manager_projects']);
foreach($projects as $short_code => $project) {
$project_names[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
};
return '{"total":' . count($project_names) . ',"results":' . $this->modx->toJSON($project_names) . ',"success":true}';
}
...
}
I use similar method to the first which saves ManagerProjects and works fine, so I don't think it has to do with the model declaration. I could easily just use the second method above since it seems to work, but I want to use the best method.
What is wrong with the first method?
Is the first method the proper way to implement SQL in the Modx processor? Or is there a better way?
We can do this task easier a little bit.
#Vasis is right but we can use base prepareRow method instead of reloading iterate method:
<?php
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public $classKey = 'ManagerProjects';
protected $projects = array();
public function prepareRow(xPDOObject $object) {
$_projects = unserialize($object->get('manager_projects'));
foreach($_projects as $short_code => $project) {
$this->projects[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
return parent::prepareRow($object);
}
public function outputArray(array $array,$count = false) {
$count = count($this->projects);
return parent::outputArray($this->projects,$count);
}
}
return 'GlobalLinkSettingsProcessor';
There we can see one of modx ‘features’. In modObjectGetListProcessor process method we can see this:
public function process() {
$beforeQuery = $this->beforeQuery();
if ($beforeQuery !== true) {
return $this->failure($beforeQuery);
}
$data = $this->getData();
$list = $this->iterate($data);
return $this->outputArray($list,$data['total']);
}
getData method returns a list of objects and it goes to iterate method (where we can check if the object is accessible and change the list of these objects on demand). If you don't have access to some of objects we'll get changed list. And it goes to outputArray method but second parameter is still old for it. So you should count them again.
This is solution is quite well but you tried to get data which is stored in object's field. So afterIteration method will be unusable for further extension in my version of processor. But who cares? :)
P.S.: About your first version of processor. modObjectGetList processor is ready for getting collection. So you have not to use getcollection method. Just add proper classKey property to it.
Another way is in modProcessor extension. It gives to you a base structure. But you can make your own kind of stuff.
Because you do it wrong! Just see this. The right way to do it, is something like this:
<?php
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public $classKey = 'ManagerProjects';
public function iterate(array $data) {
$list = array();
$list = $this->beforeIteration($list);
$this->currentIndex = 0;
/** #var xPDOObject|modAccessibleObject $object */
foreach ($data['results'] as $object) {
if ($this->checkListPermission && $object instanceof modAccessibleObject && !$object->checkPolicy('list')) continue;
$projects = unserialize($object->get('manager_projects'));
foreach($projects as $short_code => $project) {
$objectArray = array('project_name' => $project, 'project_short_code' => $short_code);
if (!empty($objectArray) && is_array($objectArray)) {
$list[] = $objectArray;
$this->currentIndex++;
}
}
}
$list = $this->afterIteration($list);
return $list;
}
}

Passing variable to view from controller

Can something like this be done? I want to pass a variable from a public function to my view.
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->variable;
$this->load->view('home_view', $home_data);
}
public function a_function() {
public $variable = "cool";
}
//EDIT//
This is what I m actually trying to accomplish and I m stuck.
get_two gets two items from a table. I want to add the two items to two variables and pass them to the view.
public function get_two() {
$get_results = $this->home_model->get_two_brands();
if($get_results != false){
$html = '';
foreach($get_results as $result){
$html .= '<li>'.$result->brand.'</li>';
}
$result = array('status' => 'ok', 'content' => $html);
header('Content-type: application/json');
echo json_encode($result);
exit();
}
}//public function get_two() {
Should I create two functions like this? But I don't know how to pass the $get_results array from get_two to the below functions. I tried public $get_results = $this->model ... etc but that didn't work.
public function result_one() {
return $resultOne = $get_results[0];
}
public function result_two() {
return $resultTwo = $get_results[1];
}
I'm not sure I've got the question correctly but what you're trying to achieve is something like this?
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->a_function();
$this->load->view('home_view', $home_data);
}
public function a_function() {
return $variable = "cool";
}
/** AFTER EDIT **/
Things get complicated (possibly because of my english comprehension).
you said
get_two gets two items from a table. I want to add the two items to two variables and pass them to the view.
So from the function get_two() you need to get and use the result in this way?
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->get_two(); // <- here?
$this->load->view('home_view', $home_data);
}
So you can try with:
public function get_two() {
$get_results = $this->home_model->get_two_brands();
if($get_results != false){
return $get_results;
}
}
and then
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->get_two();
$this->load->view('home_view', $home_data);
}
and inside you home_view :
<?php
foreach($home_data['cool'] as $result){
echo '<li>'.$result->brand.'</li>';
}
?>
/** AFTER NEW QUESTION **/
I need the ids of the two choices as two distinct variables
So change the index function this way:
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->get_two(); // <- maybe you don't need this anymore
list($result1, $result2) = $this->get_two();
$home_data['resultId1'] = $result1->id;
$home_data['resultId2'] = $result2->id;
$this->load->view('home_view', $home_data);
}
Now you're able to use $home_data['resultId1'] and $home_data['resultId1'] inside your view.
You can also define the variable in the constructor, this is one way .
CODE:
public function __construct(){
$this->variable = "cool";
}
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->variable;
$this->load->view('home_view', $home_data);
}
I don't know the codeigniter framework so this is why I asked for a part of your view but it looks pretty simple as I check in the doc. And the doc's are not bad there.
Check for Adding Dynamic Data to the View
public_function() should return something, for ex:
function public_function() {
return 'groovy';
}
Then call it in the controller:
public function index() {
$home_data['username'] = "myname";
$home_data['cool'] = $this->public_function();
$this->load->view('home_view', $home_data);
}
Then add to the view somewhere
<?php echo $home_data['cool'];?>
I assume it's wrapped in some class. So if you cannot return the value you need (for ex. function already returns something else) then do something like this:
class Someclass {
public $some_class_variable;
function public_function() {
$this->some_class_variable = 'groovy';
}
function index() {
$home_data['cool'] = $this->some_class_variable;
}
}

How to call a class member as a closure?

I'm trying to make the following example works. It looks like PHP thinks $this->getData2 as a member variable. How do I make it so that PHP thinks it as a method?
class Test {
public function getData()
{
return array(
'data1'=>array('name'=>'david'),
'data2'=>$this->getData2
);
}
public function getData2()
{
return "hello"
}
}
$test = new Test;
$data = $test->getData();
$data = $data['data2']();
I've tried the following, but looks like..I can't use $this in this case
function() use($this) {
return $This->getData2();
}
class Test {
public function getData(){
return array(
'data1'=>array('name'=>'david'),
'data2'=>'getData2'
);
}
public function getData2() {
return "hello";
}
}
$test = new Test;
$data = $test->getData();
$data = $test->$data['data2']();
echo $data;
Wasn't working without the $test-> on the $data = $test->$data['data2'](); line
And because I love fiddles: http://phpfiddle.org/main/code/4f5-v37
A callable to a method is an array with the object as a first member, and the method name as a second one.
So:
class Test {
public function getData()
{
return array(
'data1'=>array('name'=>'david'),
'data2'=>array($this, 'getData2')
);
}
public function getData2()
{
return "hello";
}
}
$test = new Test;
$data = $test->getData();
$data = $data['data2']();
Try:
class Test {
public function getData(){
return array('data1' => array('name' => 'david'), 'data2' => 'getData2');
}
public function getData2(){
return 'hello';
}
}
$test = new Test; $data = $test->getData(); echo $test->$data['data2']();
Easiest way would just be to do the calculation in a variable outside of the array and then just put the variable into the array

Zend Framework JSON Output

In controller I am generating a special form by ID, passed from AJAX. Form output is JSON. Form creates finely. But my problem is to show this JSON in view. How?
Thank you.
In controller (http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.json):
$this->getHelper('json')->sendJson(array(
'param1' => 'v1'
'param2' => 'v2'
));
In view (http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.json):
<?php
echo $this->json(array(
'param1' => 'v1'
'param2' => 'v2'
));
?>
json is a encoded string containing vars in js style if you need to access the member in this string you need to json_decode the string so
$result = json_decode($jsonString);
but note that json treat php associative array like php object ... so if you pass an array you can access it as $result->memberReference not $result['memberReference'];
The easiest way is to stop view from being executed:
function jsonAction () {
....
print $json;
exit;
}
Also see check http://pl.php.net/json_encode if you don't have JSON string already.
You can use Zend class
$sData = Zend_Json::encode($aArray);
Or you can use advanced scenario like:
$data = array(
'onClick' => new Zend_Json_Expr('function() {'
. 'alert("I am a valid javascript callback '
. 'created by Zend_Json"); }'),
'other' => 'no expression',
);
$jsonObjectWithExpression = Zend_Json::encode($data,false,
array('enableJsonExprFinder' => true)
);
The best way todo this in my opinion is to assign one controller as your json output, then you can do this:
class Api_IndexController extends Zend_Controller_Action {
public function init() {
$this->data = array();
}
public function preDispatch() {
$this->variables = $this->_getAllParams();
}
public function postDispatch() {
$this->_helper->json($this->data);
}
public function __call($name, $args) {
return;
}
public function forumAction () {
$this->mapper = new ORM_Model_Mapper_Forum();
$this->model = new ORM_Model_Forum();
$this->dbTable = new ORM_Model_DbTable_Forum();
if (isset($this->variables['id']) && is_numeric($this->variables['id'])) {
$output = $this->model->find($this->variables['id']);
if ($output->id == null) {
return $this->_setError(404);
}
} else {
$output = $this->mapper->fetchAllToArray();
}
$this->data = $output;
}
private function _setError($code=500) {
$this->data = array('error' => $code);
}
}

Categories