Dynamic variables in PHP callback function - php

I have a private function to return an array of options, those options indicate a callback and other options such as template, form, etc. Here the code:
/**
* #return array
*/
private function options()
{
$options = [
'general' => [
'form' => GeneralConfigType::class,
'template' => 'general.html.twig',
'title' => 'ConfiguraciĆ³n General',
'ignoreFields' => ['slider', 'social'],
'uploadedFields' => [],
'callbacks' => ['generalData']
],
'business' => [
'form' => ConfigurationType::class,
'template' => 'business.html.twig',
'title' => 'ConfiguraciĆ³n de Empresa',
'ignoreFields' => [],
'uploadedFields' => ['image','favicon','login_icon','sidebar_icon'],
'callbacks' => ['businessImage']
],
];
return $options;
}
Now here is my doubt, in addition to indicate the function you have to execute in the key callback, Can I pass on the variables I'm going to need in that callback? I've tried several ways and they haven't worked.
Example:
Before:
'callbacks' => ['generalData']
After:
In this example I'm assigning the '$', but I could do it if the only string, I'm just looking for a way to pass to the callback the variables it needs and no more.
'callbacks' => ['generalData' => '$configurationData, $configuration, $form, $request']
And this code would be where everything would be executed in other method:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$this->$callback($variables);
}
}

If I understand you correctly, you want to store the name of the variable in the array of options and then use that variable in the callback function.
When I've done this type of thing, I find it easier to just store the variable name as text and leave out the $ from the name stored in the array. I then use a variable variable when retrieving it.
Either way, I think you need a little more code on the execution side. One more loop:
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
foreach($variables as $variable){ // extra loop to get the variables
$this->$callback[$$variable];
// This is where it gets tricky, and depends on how you wish to format.
// The variables are currently part of an array, thus the array notation
// above. By using the stored name only, and a variable variable, you
// should be able to get to the var you need
}
}
}

#jcarlosweb, what you need to do is very simple. The short answer is that it can be done using the [call_user_func_array()][1] method.
In the context of your example, the callbacks could be rearranges in the following way ...
'callbacks' => ['generalData' => [$configurationData, $configuration, $form, $request]
Basically, the array keys will be the name of the function to call, and the corresponding array values will be a array of the values of each parameter that is accepted but the callback function. Doing it this way is important because you need to capture the value of the parameters while they are in scope. And this will avoid using eval().
Using the callbacks can be as simple as ...
$options = options();
foreach ($options['callbacks'] as $callback => $params) {
$result = call_user_func_array($callback, $params);
// Do something with $result if necessary
}

I finally got it with the function compact http://php.net/manual/en/function.compact.php
Here's the code:
First I select the variables I need in my options:
'callbacks' => ['businessImage' => ['configurationData', 'configuration', 'form', 'request']]
Second I call the variables with compact, but I had to use extract here because if I didn't configurationData variable wasn't modified, which I don't understand since I had previously referenced it.
if (!empty($options[ 'callbacks' ])) {
foreach ($options[ 'callbacks' ] as $callback => $variables) {
$variables = compact($variables);
$this->$callback($variables);
extract($variables);
}
}
Third callback applied and referenced:
/**
* #param array $params
* #return array $configurationData
*/
private function businessImage(&$params)
{
extract($params,EXTR_REFS);
// more code here ......
$configurationData[ "image" ] = $originalImageName;
$configurationData[ "favicon" ] = $originalFaviconName;
$configurationData[ "login_icon" ] = $originalLoginIconName;
$configurationData[ "sidebar_icon" ] = $originalSidebarIconName;
return $configurationData;
}
This works correctly in my website, but as I said before I do not understand why I have to call back the function extract, if I have already passed it referenced in the same callback as you see in my last code.

Related

Drupal 7: How do I get altered values from a preprocess function back to the render function?

I am trying to make a simple Panels module. I have a form with text fields were I can enter value, then that value prints to the .tpl file, via a render function, eg:
function my_module_panel_render($subtype, $conf, $args, $contexts) {
$block = new stdClass();
$block->content = [
'#theme' => 'my_tpl',
'#config' => $conf,
];
return $block;
}
Then on .tpl:
<?php print $config['name_field']; ?>
This works fine.
But I want to alter the value slightly. I've learnt I need a hook_preprocess_theme() function, which I have added.
But then how do I actually go about altering the values? How do I then return the altered value to $conf?
Doing something like
$conf['name_field'] = $conf['name_field'] . $some_other_stuff;
Doesn't seem to work.
Would anyone know what I could do?
OK, what I didn't relise was that $conf was a sub-array of the $variables array, which hook_preprocess_theme() takes as a parameter, as seen on my hook_theme:
function my_module_theme() {
return [
'my_module' => [
'template' => 'theme/my_theme',
'render element' => 'element',
'variables' => [
'config' => NULL,
],
],
];
}
So, my hook_preprocess_theme() function now looks like:
function my_module_preprocess_my_theme(&$vars) {
$conf = $vars['config'];
$vars['config']['name_field'] = $conf['name_field'] . $some_other_stuff;
}

Laravel 5 passing page info with array

I'm trying to pass data about the page state (navbar links having active class when you are in that exact page), page title. I do so with an indexed array $pageInfo, however I am getting a syntax error and doen't know where?
Also do you think this is a good method or should I use view->share() instead?
public function clases()
{
$pageInfo[] =
(
'page_title' => 'Clases',
'menu_active' => 'CLases',
'sub_menu_active' => '',
);
return view('clases.list', compact('pageInfo'));
}
public function domicilio()
{
$pageInfo[] =
(
'page_title' => 'Clases a domicilio',
'menu_active' => 'Clases',
'sub_menu_active' => 'Clases a domicilio',
);
return view('clases.domicilio', compact('pageInfo'));
I suggest you read PHP basic syntax.
Basically you want to do this:
$pageInfo =
[
'page_title' => 'Clases',
'menu_active' => 'CLases',
'sub_menu_active' => '',
];
Arrays have a syntax of [key => val, ...] in PHP, you're using () as it seems.
Also $someArray[] = someValue, will append the someValue to an existing array, in your case that would create another, unwanted level of your array.
And last, you're not ending the domicilio() function. But I'll assume you just didn't paste it in (you should add } at the end, if that's not the case).

CSV Import in SilverStripe Duplicates and RelationCallbacks

I need to understand the code below, specially how exactly $duplicateChecks and $relationCallbacks work but there is little explanation on the official documentation. Can somebody explain how these work or suggest some other documentation I can look at?
class PlayerCsvBulkLoader extends CsvBulkLoader {
public $columnMap = array(
'Number' => 'PlayerNumber',
'Name' => '->importFirstAndLastName',
'Birthday' => 'Birthday',
'Team' => 'Team.Title',
);
public $duplicateChecks = array(
'Number' => 'PlayerNumber'
);
public $relationCallbacks = array(
'Team.Title' => array(
'relationname' => 'Team',
'callback' => 'getTeamByTitle'
)
);
public static function importFirstAndLastName(&$obj, $val, $record) {
$parts = explode(' ', $val);
if(count($parts) != 2) return false;
$obj->FirstName = $parts[0];
$obj->LastName = $parts[1];
}
public static function getTeamByTitle(&$obj, $val, $record) {
return FootballTeam::get()->filter('Title', $val)->First();
}
}
$duplicateChecks is used by findExistingObject function in the CsvBulkLoader class. It is iterated over to find any object that has a column with the specified value. In that example, it checks the "PlayerNumber" column.
It can also be passed a callback like so:
public $duplicateCheck = array(
'Number' => array(
'callback' => 'checkPlayerNumberFunction'
)
);
The callback specified needs to either exist on an instance of the class specified on the property objectClass or on the CsvBulkLoader itself (which would happen if you extended it). These callbacks are used to do more complex duplicate lookups and return an existing object (if any) found.
$relationCallbacks on the other hand is used by the main processRecord function. The callback works in the same way as the $duplicateCheck callback, it needs to either exist on an instance of the class specified on the proeprty objectClass or on the CsvBulkLoader. These callbacks can return an object that will be related back to a specific object record (new or existing) as a has_one.
There is a little more to it than that though the best way to learn is by a bit of experimentation and jumping through the code of the class itself. I have linked to the various functions etc in my answer.

ZF2: How to pass parameters to forward plugin which I can then get in the method I forward them to?

I have an Action method in Foo Controller which requires parameters:
public function fooAction($one, $two) {
$a = one;
$b = $two;
}
And I need to forward to that method from the other method of some Boo Controller. And one of those parameters has to be by reference parameter. The only example that the manual has is this:
$result = $this->forward()->dispatch('Boo\Controller\Boo', array('action' => 'boo'));
No any additional parameters. But they write:
$params is an optional array of parameters with which to see a
RouteMatch object for purposes of this specific request.
So, I tried:
$result = $this->forward()->dispatch('Boo\Controller\Boo', array(
'action' => 'boo',
'one' => &$one,
'two' => $two,
));
But it doesn't work.
Is there any way to pass additional parameters to forward controller?
UPD:
These do not work too:
$result = $this->forward()->dispatch('Boo\Controller\Boo', array(
'action' => 'boo',
'params' => array(
'one' => &$one,
'two' => $two,
)));
$result = $this->forward()->dispatch('Boo\Controller\Boo', array(
'action' => 'boo',
'options' => array(
'one' => &$one,
'two' => $two,
)));
UPD 2:
I still can't get the functionality I want (to pass parameters with the forward plugin) but I found other solutions. Before calling the forward plugin I set the variables to the Request object and after the forward I get them from the Request in my boo Action of my Boo\Controller\BooController:
// in Foo::fooAction
$this->getRequest()->one = &$one;
$this->getRequest()->two = $two;
$result = $this->forward()->dispatch('Boo\Controller\Boo', array('action' => 'boo'));
// in Boo::booAction
$a = $this->getRequest()->one;
$b = $this->getRequest()->two;
Stupid solution, it will not work with Ajax requests. Still interested how to pass parameters with the forward plugin. OR MAYBE how to get them in the booAction. Because there in no anything in the Request if I pass them with the forward.
UPD 3 and Final:
I finally found where they've decided to hide parameters I pass with the forward plugin. They put them in the RouteMatch object.
- Tryyyy to guess where we've hidden your params... Oh yeeah, they are in the RouteMatch, of course they are there, didn't you think smth else?
And NO ANY info in the forward plugin section of the manual!
To get params, I have to do this in my BooController::booAction:
$param = $this->getEvent()->getRouteMatch()->getParam('nameOfParam');
Why not to use the params plugin?
This works for me:
public function indexAction() {
$object = new SomeObject();
return $this->forward()->dispatch('Application\Controller\Index', [
'action' => 'show',
'myObject' => $object,
]);
}
public function showAction() {
$object = $this->params('myObject');
var_dump($object);
return [];
}
You can create a container class and use it in both controllers
in module.conf
public function getServiceConfig()
{
return array(
'invokables' => array(
'my_handy_container' => 'path\container_class_name',
)
);
}
Create a getter in both controllers:
public function getMyHandyContainer()
{
if (!$this->myHandyContainer) {
$this->myHandyContainer = $this->getServiceLocator()->get('my_handy_container');
}
return $this->myHandyContainer;
}
And call it using:
$myContainer = $this->getMyHandyContainer()->myHandyContainer;
$myContainer->foo = 5; // set something
ZF2 way to pass vars using forward
In the passing method do:
return $this->forward()->dispatch('controller_name', [
'action' => 'whatever',
'varname' => $value,
'varname2' => $value2
]);
In the invoked controller method, do:
$param2 = $this->params()->fromRoute('varname2',false);
Thought I would add another option that works for me.
You can simply pass the params straight through the forward function and use the routeMatch function to access them at the other end.
return $this->forward()
->dispatch('Module\Controller\Foo', array(
'action' => 'bas',
'id' => 6)
);
Passes to Foo Controller, basAction in this method you can then use the following code to access the id param
$myParam = (int) $this->getEvent()->getRouteMatch()->getParam('id');
Not sure if this meets your requirements - but works for me.
Thanks for the question, helped me a lot. Found an easy way for getting all params passed to forward()->dispatch(...). In the controller's action method:
$params = $this->params()->fromRoute();
returns array $data as passed as $data into forward()->dispatch($controllerName, $data).
Here in the official ZF2 documentation is written exactly how it works:
$params is an optional array of parameters with which to seed a RouteMatch object for purposes of this specific request. Meaning the parameters will be matched by their key to the routing identifiers in the config (otherwise non-matching keys are ignored).
So pass like this:
$params = array(
'foo' => 'foo',
'bar' => 'bar'
);
$this->forward()->dispatch('My\Controller', $params)
And then you can get your route match params in your My\Controller like normally:
$foo = $this->params()->fromRoute('foo');
$bar = $this->params()->fromRoute('bar');
For people struggling with accessing parameters within their controller here a nice overview from this CheatSheet.
$this->params()->fromPost('foo'); //POST
$this->params()->fromQuery('foo'); //GET
$this->params()->fromRoute('foo'); //RouteMatch
$this->params()->fromHeader('foo');//Header
$this->params()->fromFiles('foo'); //Uploaded file

Can't access global PHP array variable

Within a Drupal module callback function, there is a simple custom function that intakes an array.
The custom function executes correctly when I define the input array within the Drupal module callback function. However, when I define the input array at the root level (global), the custom function within the Drupal module callback function fails.
As a test, I made the custom function simply output the contents of the input array as a string. The first method outputs correctly while the second method does not have any output. Ideally, I'd like to define the array at the global level so that it can be used by other functions.
Thoughts?
<?php
// ** Placement of array for method 2
$mapping = array(
0 => "name",
1 => "match"
);
function mymodule_menu() {
$items = array();
$items['mymodule'] = array(
'title' => 'MyModule',
'page callback' => 'myModule_main',
'access callback' => TRUE,
'type' => MENU_NORMAL_ITEM
);
return $items;
}
function myModule_main() {
// ** Placement of array for method 1
$mapping = array(
0 => "name",
1 => "match"
);
$output = myFunction($mapping);
echo $output; // ** Returned to client side via AJAX
}
You need to "import" the global variable into the function's scope using the global keyword.
See http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global
function myModule_main() {
global $mapping;
...
}
<?php
global $foobar;
$foobar = "text";
function myFunction() {
echo $GLOBALS["foobar"]; // Returns "text"
}
?>

Categories