slim flash: Passing an array - arrays

I want to pass an array using the Slim Framework Flash Messages service provider but my array is converted to a string and therefore the whole thing falls short...
If I understand correctly the code for the addMessage method it should be working, except it's not.
Here a little example:
session_start();
$app = new \Slim\App(['settings' => ['displayErrorDetails' => true]]);
$container = $app->getContainer();
$container['flash'] = function() {
return new \Slim\Flash\Messages();
};
$app->get('/foo', function ($request, $response, $args) {
$this->flash->addMessage('test', ['key' => 'value']);
return $response->withStatus(302)->withHeader('Location', '/bar');
});
$app->get('/bar', function ($request, $response, $args) {
$messages = $this->flash->getMessages();
print_r($messages); // returns Array ( [test] => Array ( [0] => Array ) )
});
$app->run();
Am I missing something?

It's related to the version of slim-flash that you are using. If you check the differences between the current (only one) version (0.1.0) and master branch, you'll discover that the bug you're facing was fixed in PR 12.
While we wait for another release, you can temporarily use the dev-master version in composer.json and all will work fine:
"slim/flash": "dev-master"

Related

CakePHP 4.1.4 - How to create, read and check cookies in the new version of CakePHP

In CakePHP 3.8 the segment of my controller looks like this:
// ...
public function beforeFilter(Event $event)
{
// ...
$this->Cookie->configKey('guestCookie', [
'expires' => '+1 days',
'httpOnly' => true,
'encryption' => false
]);
if(!$this->Cookie->check('guestCookie')) {
$guestID = Text::uuid();
$this->Cookie->write('guestCookie', $guestID);
}
$this->guestCookie = $this->Cookie->read('guestCookie');
// ...
}
public function error()
{
// ...
$this->Cookie->delete('guestCookie');
// ...
}
// ...
How to write the same thing in CakePHP4 version? My problem relates to defining Cookies.
Cookie settings are described here:
https://book.cakephp.org/4/en/controllers/request-response.html#cookie-collections
, but unfortunately this didn't help me at all.
I tried to solve the problem on this way:
public function beforeFilter(EventInterface $event)
{
//...
if(!$this->cookies->has('guestCookie')) {
$cookie = (new Cookie('guestCookie'))->withValue(Text::uuid())->withExpiry(new \DateTime('+20 days'))->withPath('/')->withSecure(false)->withHttpOnly(true);
$this->cookies = new CookieCollection([$cookie]);
}
$this->guestCookie = $this->cookies->get('guestCookie')->getValue();
//...
}
In my case $ this->cookies->has('guestCookie') is always 'false'.
The cookie value is never stored in the browser.
Please help.
There's rarely a need to touch cookie collections, most of the times simple reading of cookie values from the request object, and writing cookies to the response object is all you need, so I would suggest that you stick with that until the actual need for collections comes up.
The docs could probably do a better job here at explaining when to use what.
Reading, writing, and deleting cookies
As shown in the linked docs, cookie values can be read via:
$this->request->getCookie($cookieName)
and written via:
$this->response = $this->response->withCookie($cookieObject)
It's important to reassign the response object (unless you directly return it from the controller), as it is immutable, meaning withCookie() will return a new response object instead of modifying the current one.
Deleting cookies can be done by responding with an expired cookie, using withExpiredCookie() instead of withCookie(), or obtaining the expired version of a cookie via $cookie->withExpired() and passing it to withCookie().
Configuring cookie defaults
If you wanted to, cookie defaults can be set via Cookie::setDefaults():
\Cake\Cookie\Cookie::setDefaults([
'expires' => new DateTime('+1 days'),
'http' => true,
]);
However this will apply application wide to all cookie instances being created after this point, so you'd likely use it rather rarely, and if you do, do so with care!
Porting from the Cookie component
With the new API, your code could be written like this, with $this->guestCookie holding the cookie value, either the newly generated one, or the one obtained from the cookie received by your application:
use Cake\Http\Cookie\Cookie;
// ...
public function beforeFilter(Event $event)
{
// ...
$guestID = $this->request->getCookie('guestCookie');
if(!$guestID) {
$guestID = Text::uuid();
$cookie = Cookie::create('guestCookie', $guestID, [
'expires' => new DateTime('+1 days'),
'http' => true,
]);
$this->response = $this->response->withCookie($cookie);
}
$this->guestCookie = $guestID;
// ...
}
public function error()
{
// ...
$cookie = new Cookie('guestCookie');
$this->response = $this->response->withExpiredCookie($cookie);
// ...
}
// ...
See also
Cookbook > Request & Response Objects > Request > Cookies
Cookbook > Request & Response Objects > Response > Setting Cookies

Exporting an array within an ".then" doesnt work

I'm new to NodeJS and are only familiar with Java. I'm trying to create a file that creates objects based on a database and adds them to an array. This array I want to be able to export so that I can use it throughout the whole program, but when I try to export the array it doesn't work. I've tried googling and understanding but haven't come across anything that was helpful unfortunately.
I hope that someone can help me understand
I've tried calling module.exports after the ".then" call, but it just returns an empty array because its async.
I've also tried calling module.exports = teams inside the .then call but it didn't work neither.
var teams = [];
function assignTeamsToClasses() {
return new Promise((resolve, reject) => {
getAllTeamsInDb((teamList) => {
teamList.forEach((aTeam) => {
let newTeam = new Team(aTeam['teamid'], aTeam['teamname'], aTeam['teamrank']);
teams.push(newTeam);
});
resolve();
});
})
}
assignTeamsToClasses().then(() => {
module.exports = teams;
});
main.js
var teams = require('./initialize.js');
console.log(teams);
I expect it to return all teams that are in the database. I know that array is not empty when called within the ".then" call, but the export part does not.
Simple
the sequence require() + console.log() is synchronous
assignTeamsToClasses() is asynchronous, i.e. it updates teams at some unknown later point in time.
You'll have to design your module API to be asynchronous, e.g. by providing event listener interface or Promise interface that clients can subscribe to, to receive the "database update complete" event.
A proposal:
module.exports = {
completed: new Promise(resolve =>
getAllTeamsInDb(teams => {
const result = [];
teams.each(aTeam =>
result.append(new Team(aTeam.teamid,
aTeam.teamname,
aTeam.teamrank)
)
);
resolve(result);
})
),
};
How to use it:
const dbAPI = require('./initialize.js');
dbAPI
.completed
.then(teams => console.log(teams))
.catch(error => /* handle DB error here? */);
Every caller who uses this API will
either be blocked until the database access has been completed, or
receive result from the already resolved promise and proceed with its then() callback.

Cakephp 2.3 beforeFilter and implementedEvents aren't able to co-exist

Using cakephp 2.3.5.
I use beforeFilter in several controllers to allow certain actions without the need to log in. I've used it successfully for quite some time. However, I've noticed that I can't get beforeFilter to fire if the controller also has the implementedEvents() method.
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('matchWwiProducts');
}
public function implementedEvents() {
return array(
'Controller.Product.delete' => 'deleteSku',
'Controller.Product.price' => 'notifySubscribers',
'Controller.Product.stock' => 'notifySubscribers'
);
}
For the code as displayed above I will be forced to do a login if I call the method www.example.com/products/matchWwiProducts.
When I comment out the implementedEvents() everything works as intended. I've searched around and can't find any references to implementedEvents() creating issues with beforeFilter.
The action matchWwiProducts() is as follows. It works perfectly when I log in. However, I don't want to force a log in for this action to take place.
public function matchWwiProducts() {
// this is an audit function that matches Products sourced by wwi
//
$this->autoRender = false; // no view to be rendered
// retrieve products sourced from wwi from Table::Product
$this->Product->contain();
$wwiProducts = $this->Product->getWwiSkus();
$wwiProductCount = count($wwiProducts);
// retrieve products sourced from wwi from Table:Wwiproduct
$this->loadModel('WwiProduct');
$this->Wwiproduct->contain();
$wwiSource = $this->Wwiproduct->getSkuList();
$wwiSourceCount = count($wwiSource);
// identify SKUs in $wwiProducts that are not in $wwiSource
$invalidSkus = array_diff($wwiProducts, $wwiSource);
// identify SKUs in $wwiSource that are not in $wwiProducts
$missingSkus = array_diff($wwiSource, $wwiProducts);
$missingSkuDetails = array();
foreach ($missingSkus as $missingSku) {
$skuStockStatus = $this->Wwiproduct->getStockStatus($missingSku);
$missingSkuDetails[$missingSku] = $skuStockStatus;
}
$email = new CakeEmail();
$email->config('sourcewwi');
$email->template('sourcewwiaudit', 'sourcewwi');
if (count($invalidSkus) > 0 || count($missingSkus) > 0) {
$email->subject('WWI Source Audit: Invalid or Missing SKUs');
$email->viewVars(array('invalidSkus' => $invalidSkus,
'missingSkuDetails' => $missingSkuDetails,
'wwiProductCount' => $wwiProductCount,
'wwiSourceCount' => $wwiSourceCount));
} else {
$email->subject('WWI Source Audit: No Exceptions');
$email->viewVars(array('wwiProductCount' => $wwiProductCount,
'wwiSourceCount' => $wwiSourceCount));
}
$email->send();
}
It doesn't fire because you're overloading the implementendEvents() method without making sure you keep the existing events there.
public function implementedEvents() {
return array_merge(parent::implementedEvents(), array(
'Controller.Product.delete' => 'deleteSku',
'Controller.Product.price' => 'notifySubscribers',
'Controller.Product.stock' => 'notifySubscribers'
));
}
Overloading in php.
Check most of the base classes, Controller, Table, Behavior, Component, they all fire or listen to events. So be careful when extending certain methods there. Most simple way might to do a search for "EventListenerInterface" in all classes. A class that implements this interface is likely to implement event callbacks.

Drupal 7 - fatal error passing a variable from custom module to a template

I am porting a module from Drupal 6 to Drupal 7 and I am trying to pass a variable from my custom module to a template. I have something like this:
function my_callback_function(){
... //some unrelated code
$page_params = array();
$page_params['items_per_page'] = 25;
$page_params['page'] = $_GET['page'] ? $_GET['page'] : 0;
$page_params['total_items'] = $data_provider->getNumItems();
$page_params['total_pages'] = $data_provider->getNumPages($page_params['items_per_page']);
return theme('my_theme', $page_params);
}
function my_module_theme($existing, $type, $theme, $path) {
return array(
'my_theme' => array(
'variables' => array('page_params' => NULL),
'template' => 'theme/my_template_file',
),
);
}
And inside *my_template_file.tpl.php* I try to use $page_params:
<?php print $page_params['total_items']; ?>
All that make my site to throw the following error:
Fatal error: Unsupported operand types in
C:...\includes\theme.inc on line 1075
Which corresponds with these lines of code in theme.inc:
// Merge in argument defaults.
if (!empty($info['variables'])) {
$variables += $info['variables']; // THIS IS THE VERY EXACT LINE
}
elseif (!empty($info['render element'])) {
$variables += array($info['render element'] => array());
}
If I leave the theme() call as it was in Drupal 6, the error doesn't appear but then my template doesn't recognize my $page_params variable:
return theme('my_theme', array('page_params' => $page_params));
I have read half the API trying to figure out what I am doing wrong but as far as I have read, it seems that this is the proper way to pass variables from a custom module to a template.
Thus, any kind of help will be more than welcome.
Thanks in advance.
Finally, I figured out what I was doing wrong. In fact, they were a couple of things:
My theme() call was ok:
return theme('my_theme', $page_params);
But my hook_theme implementation wasn't. If $page_params is my array of variables, i cannot use the whole array as a variable, I have to explicitly specify which are my variables inside the array. Something like this:
function my_module_theme($existing, $type, $theme, $path) {
return array(
'my_theme' => array(
'variables' => array(
'items_per_page' => NULL,
'page' => NULL,
'total_items' => NULL,
'total_pages' => NULL,
),
'template' => 'theme/my_template_file',
);
}
And finally, inside my_template_file.tpl.php I will have to use the variable names directly instead of using them as a component of $page_params:
<?php print $total_items; ?>
It may seem obvious for experienced users but it took me a while until I realized this. I hope it can be useful for other beginners like me.
You can use drupal variable_set() and variable_get() to store data in drupal session and get data from session.
Thanks

Drupal 7: Drupal.behaviors isn't running

I've got a custom module I'm creating called touchpoints. At the top of the touchpoints.module file I have the following:
global $base_path;
$my_settings = array(
'basepath' => $base_path,
'module_path' => drupal_get_path('module','touchpoints')
);
drupal_add_js(array('touchpoints' => $my_settings), 'setting');
drupal_add_js(drupal_get_path('module','touchpoints') . '/touchpoints.js');
Then in my touchpoints.js file I have the following:
Drupal.behaviors.touchpoints = function(context){
$('.form-item-touchpointlocation').css('display','block');
$('.form-item-touchpointcategory').css('display','none');
}
It's my understanding that anything inside the Drupal.behaviors call should run when the DOM finishes loading similar to a $(document).ready call. However, this code isn't being executed. Just to be sure I put an alert in the function as well, and it wasn't being triggered. Is there a step I'm missing?
I realized I was using Drupal 6 syntax. In Drupal 7 you have to declare behaviors differently. It should have looked like this
(function ($) {
Drupal.behaviors.touchpoints = {
attach: function (context, settings) {
$('.form-item-touchpointlocation').css('display','block');
$('.form-item-touchpointcategory').css('display','none');
}
};
})(jQuery);
For reference see: http://drupal.org/node/756722

Resources