In Zf2 application written the model file to retrieve the data set from the table,
it works as expected for returning one result set, but for returning multiple rows not able to achieve by the below code.
Working and Returning single row
/**
* #param $id
* #return bool|Entity\Feeds
*/
public function getAppFeed($id)
{
$row = $this->select(array('app_id' => (int)$id))->current();
if (!$row)
return false;
$feedVal = new Entity\Feeds(array(
'id' => $row->id,
'title' => $row->title,
'link' => $row->link,
'Description' => $row->description,
'created' => $row->created,
));
return $feedVal;
}
Removed current and tried tablegateway object also but throwing the error.
Feeds table will have multiple record for each of the application, I need a function to achieve the same.
The Select always returns a ResultSet. You can access the objects(1) of ResultSet by iterating over it, because it implements the Iterator Interface.
Just an example piece of code:
public function getAppFeed($id)
{
$resultSet = $this->select(array('app_id' => (int)$id));
if ($resultSet instanceof \Zend\Db\ResultSet) {
foreach($resultSet as $item) {
// do your feed stuff here
// e.g. $item->id
}
} else {
return false;
}
}
(1) Object: meaning whatever object you asigned as Prototype in your TableGateway.
For further details, please checkout the documentation of ResultSet.
Related
I want to add to a Laravel query using a callable.
For example, I have a string for sorting records. Based on this string, I want to add to my query via an array:
public $sort = 'Newest';
public function sortQuery(Builder $query)
{
return [
'Name' => $query->orderBy('name'),
'Newest' => $query->orderBy('created_at'),
'Oldest' => $query->orderByDesc('created_at'),
];
}
public function paginateQuery()
{
$query = User::query();
foreach ($this->sortQuery($query) as $key => $value) {
if ($this->sort == $key) {
$query = $value;
}
}
return $query->paginate();
}
In this example, when I run $this->paginateQuery() it does not sort as desired.
I've also tried $query = $this->sortQuery($query)[$this->sort]; instead of the foreach loop and the result is the same.
How would I chain $value onto the $query based on the array key?
You can amend your function slightly to apply the sorting immediately, for instance like so:
public function sortQuery(Builder $query, $sortKeys)
{
// Define a map to find the options for your specific sorting key.
$map = [
'Name' => ['name', 'ASC'],
// Note: I flipped this around, Newest first means "descending date".
'Newest' => ['created_at', 'DESC'],
'Oldest' => ['created_at', 'ASC'],
];
// Loop the given sortkeys. The (array) cast allows you to pass a string as well.
foreach((array) $sortKeys as $sortKey) {
// Check if map exists.
if(isset($map[$sortKey])) {
// Use the splat operator to pass the map values as arguments to the orderBy function
// (the second argument can be ASC/DESC)
$query->orderBy(...$map[$sortKey]);
}
}
You can even define it as a query scope in one of your models (preferably a parent class that is inherited by multiple Eloquent models), see https://laravel.com/docs/8.x/eloquent#local-scopes:
// Model.php
/**
* The prefix `scope` is required. It can be called as `$query->sortMe(...)`.
*/
public function scopeSortMe(Builder $query, $sortKeys)
{
// ... same code
}
// SomeController.php
// Calling it only requires the additional parameters, not the $query object.
$query->sortMe($sortKeys);
I am working on a program where we need to process very large JSON file, so I would like to use a streaming event oriented reader (like jsonstreamingparser) so that we can avoid loading the entire structure into memory at one time. Something I'm concerned about though is the object structure that seems to be required to make this work.
For example, say I'm writing a program like Evite to send out invitations to an activity, with a JSON structure like:
{
"title": "U2 Concert",
"location": "San Jose",
"attendees": [
{"email": "foo#bar.com"},
{"email": "baz#bar.com"}
],
"date": "July 4, 2015"
}
What I would like to do is have a programming "event" that when the stream encounters a new attendee, sends out an invite email. But, I can't do that because the stream has not yet reached the date of the event.
Of course, given the example, it's fine to just read everything into memory - but my dataset has complex objects where the "attendees" attribute are, and there can be tens of thousands of them.
Another "solution" is to just mandate: you HAVE to put all the required "parent" attributes first, but that is what I'm trying to find a way around.
Any ideas?
This is another 'tree walking' problem. The JSON streaming parser reads the source file and starts to build the 'tree'. It does this by collecting 'elements' and storing them in memory. To enable us to process each entry, it 'emits events' at convenient times. Which means it will call your functions passing useful values as required.
Examples of 'tree events' are:
start_object()
end_object()
start_array()
end_array()
...
The 'example' code provided with the 'Parser' is a program that uses the Parser to build the tree in memory. I just modified that example to call our function whenever it has a 'complete Event' stored.
So, how do we identify a 'complete Event'?
The input file consist of an array where each entry is a JSON 'obbject'. Each object consists of 'sub entries' that make up the data of the 'object'.
Now, as we traverse the 'tree' building it, our code will be called at various points as shown above. Specifically when 'starting' and 'ending' of objects and arrays. We need to collect all the data for the 'outer object'.
How do we identify this? We record where we are in the 'tree' as the processing proceeds. This we do by keeping track of the depth of 'nesting' in the tree. Hence the 'levels'. The 'start' of an object 'nests' down one level, the 'end' of an object 'unnests' one level.
The objects we are interested in are a 'level 1'.
The code provided:
1) keeps track of the 'levels' and calls our function when it reaches the end of an object that is at 'level 1'.
2) Accumulates the data in the appropriate structure from the start of the the object at 'level 1'.
Requirements:
1) Call a 'callable' routine when there is a 'complete Event' that can be processed.
Assumptions:
The input file consists of an array of 'Events'.
Processing:
Parse the file
Whenever the current Event is 'complete'
Execute the 'processEvent' callable with access to the current Event.
Source Code:
Source: class Q31079129Listener at Pastebin.com
Source: index.php file at Pastebin.com
Source: test datafile : Q31079129.json at Pastebin.com
Demonstration using the code
Code: index.php
<?php // https://stackoverflow.com/questions/31079129/how-to-handle-nested-objects-in-processing-a-json-stream
require_once __DIR__ .'/vendor/jsonstreamingparser/src/JsonStreamingParser/Parser.php';
require_once __DIR__ .'/vendor/jsonstreamingparser/src/JsonStreamingParser/Listener/IdleListener.php';
require_once __DIR__ .'/Q31079129Listener.php';
/**
* The input file consists of a JSON array of 'Events'.
*
* The important point is that when the file is being 'parsed' the 'listener' is
* 'walking' the tree.
*
* Therefore
* 1) Each 'Event' is at 'level 1' in the tree.
*
* Event Level Changes:
* Start: level will go from 1 => 2
* End: level will go from 2 => 1 !!!!
*
* Actions:
* The 'processEvent' function will be called when the
* 'Event Level' changes to 2 from 1.
*
*/
define('JSON_FILE', __DIR__. '/Q31079129.json');
/**
* This is called when one 'Event' is complete
*
* #param type $listener
*/
function processEvent($listener) {
echo '<pre>', '+++++++++++++++';
print_r($listener->get_event());
echo '</pre>';
}
// ----------------------------------------------------------------------
// the 'Listener'
$listener = new Q31079129Listener();
// setup the 'Event' Listener that will be called with each complete 'Event'
$listener->whenLevelAction = 'processEvent';
// process the input stream
$stream = fopen(JSON_FILE, 'r');
try {
$parser = new JsonStreamingParser_Parser($stream, $listener);
$parser->parse();
}
catch (Exception $e) {
fclose($stream);
throw $e;
}
fclose($stream);
exit;
Code: Q31079129Listener.php
<?php // // https://stackoverflow.com/questions/31079129/how-to-handle-nested-objects-in-processing-a-json-stream
/**
* This is the supplied example modified:
*
* 1) Record the current 'depth' of 'nesting' in the current object being parsed.
*/
class Q31079129Listener extends JsonStreamingParser\Listener\IdleListener {
public $whenLevelAction = null;
protected $event;
protected $prevLevel;
protected $level;
private $_stack;
private $_keys;
public function get_event() {
return $this->event;
}
public function get_prevLevel() {
return $this->prevLevel;
}
public function get_level() {
return $this->prevLevel;
}
public function start_document() {
$this->prevLevel = 0;
$this->level = 0;
$this->_stack = array();
$this->_keys = array();
// echo '<br />start of document';
}
public function end_document() {
// echo '<br />end of document';
}
public function start_object() {
$this->prevLevel = $this->level;
$this->level++;
$this->_start_complex_value('object');
}
public function end_object() {
$this->prevLevel = $this->level;
$this->level--;
$this->_end_complex_value();
}
public function start_array() {
$this->prevLevel = $this->level;
$this->level++;
$this->_start_complex_value('array');
}
public function end_array() {
$this->prevLevel = $this->level;
$this->level--;
$this->_end_complex_value();
}
public function key($key) {
$this->_keys[] = $key;
}
public function value($value) {
$this->_insert_value($value);
}
private function _start_complex_value($type) {
// We keep a stack of complex values (i.e. arrays and objects) as we build them,
// tagged with the type that they are so we know how to add new values.
$current_item = array('type' => $type, 'value' => array());
$this->_stack[] = $current_item;
}
private function _end_complex_value() {
$obj = array_pop($this->_stack);
// If the value stack is now at level 1 from level 2,
// we're done parsing the current complete event, so we can
// move the result into place so that get_event() can return it. Otherwise, we
// associate the value
// var_dump(__FILE__.__LINE__, $this->prevLevel, $this->level, $obj);
if ($this->prevLevel == 2 && $this->level == 1) {
if (!is_null($this->whenLevelAction)) {
$this->event = $obj['value'];
call_user_func($this->whenLevelAction, $this);
$this->event = null;
}
}
else {
$this->_insert_value($obj['value']);
}
}
// Inserts the given value into the top value on the stack in the appropriate way,
// based on whether that value is an array or an object.
private function _insert_value($value) {
// Grab the top item from the stack that we're currently parsing.
$current_item = array_pop($this->_stack);
// Examine the current item, and then:
// - if it's an object, associate the newly-parsed value with the most recent key
// - if it's an array, push the newly-parsed value to the array
if ($current_item['type'] === 'object') {
$current_item['value'][array_pop($this->_keys)] = $value;
} else {
$current_item['value'][] = $value;
}
// Replace the current item on the stack.
$this->_stack[] = $current_item;
}
}
How can I return data from an external source as a DocumentSet?
I set up a custom data source to interface with Amazon's Product Advertising API. To do this, I subclassed lithium\data\source\Http and redefined the read method to suit my needs as described in the documentation (http://li3.me/docs/manual/working-with-data/creating-data-sources.wiki).
However, my lithium version (0.11, last release) does not seem to have a cast method like in the example and if I create one it won't get called when I do return $this->item($model, $data, $options).
So, I made a custom item function to create the Documents by calling parent::item just like the documentation example does for cast.
Then, after the recursive calls, I end up with an array of Document objects and the final call to parent::item then gives me an empty DocumentSet object.
How should I pass the data on to create a proper DocumentSet?
Here's a minimal example of my code:
// Within class Amazon extends \lithium\data\source\Http
protected function _init() {
// Define entity classes.
$this->_classes += array(
'entity' => 'lithium\data\entity\Document',
'set' => 'lithium\data\collection\DocumentSet'
);
parent::_init();
}
public function read($query, array $options = array()) {
// Extract from query object.
$parameters = $query->export($this, array('keys' => array('conditions')));
$conditions = $parameters['conditions'];
// Code stripped to validate conditions and prepare Amazon request (that part works).
// results in a $queryString variable.
// Get response from Server.
$xml = simplexml_load_string($this->connection->get($this->_config['basePath'], $queryString));
// Stripped response validation and reformatting -> $items contains an array of SimpleXMLElement objects.
return $this->item($query->model(), $items, array('class' => 'set'));
}
public function item($model, array $data = array(), array $options = array()) {
// Recursively create Documents for arrays.
foreach($data as $key => $value) {
if(is_array($value)) {
$data[$key] = $this->item($model, $value, array('class' => 'entity'));
}
else if(is_object($value) && get_class($value) == "SimpleXMLElement") {
// Stripped code to extract data from XML object and put it in array $docData.
$data[$key] = $this->item($model, $docData, array('class' => 'entity'));
}
}
// Works perfectly for every (recursive) call with $options['class'] == 'entity' but fails for the final call with $options['class'] == 'set' (for this final call $data contains an array of Document objects).
return parent::item($model, $data, $options);
}
I would track the master branch instead of the release versions.
In your case, since you're boxing your objects manually, I would do something like:
return $this->_instance('set', compact('data'));
I'm using FilterIterator to filter out the values and implemented the accept() method successfully. However I was wondering how would it be possible to get the values that returned false from my accept method in single iteration. Let's take the code below as an example (taken from php.net);
class UserFilter extends FilterIterator
{
private $userFilter;
public function __construct(Iterator $iterator , $filter )
{
parent::__construct($iterator);
$this->userFilter = $filter;
}
public function accept()
{
$user = $this->getInnerIterator()->current();
if( strcasecmp($user['name'],$this->userFilter) == 0) {
return false;
}
return true;
}
}
On the code above, it directly filters out the values and returns the values that pass from the filteriterator. Implemented as;
$array = array(
array('name' => 'Jonathan','id' => '5'),
array('name' => 'Abdul' ,'id' => '22')
);
$object = new ArrayObject($array);
$iterator = new UserFilter($object->getIterator(),'abdul');
It will contain only the array with name Jonathan. However I was wondering would it be possible to store the object with name Abdul in another variable using the same filter with a slight addition instead of reimplementing the entire filter to do the opposite?. One way I was thinking would exactly copy paste the FilterIterator and basically change values of true and false. However are there any neat ways of doing it, since it will require another traversal on the list.
I think you must rewrite the accept() mechanic. Instead of returning true or false, you may want to break down the array to
$result = array(
'passed' => array(...),
'not_passed' => array(...)
);
Your code may look like this
if (strcasecmp($user['name'], $this->userFilter) == 0) {
$result['not_passed'][] = $user;
} else {
$result['passed'][] = $user;
}
return $result;
I'm creating a forum, and I want to keep track of which threads have been updated since the user last visited. So I have an array that I keep in $_SESSION that is basically structured as [$boardid][$threadid] = 1. If the threadid and boardid are set, then the thread has not been read and the board contains unread threads. When a user views a thread, I just unset() the appropriate board and thread id. However, I've having problems with getting unset to work with arrays like this.
Firstly, I have a session class to make handling session data a little nicer
class Session {
private $_namespace;
public function __construct($namespace = '_default') {
$this->_namespace = $namespace;
}
/**
* Erase all variables in the namespace
*/
public function clear() {
unset($_SESSION[$this->_namespace]);
}
public function __set($name, $value) {
$_SESSION[$this->_namespace][$name] = $value;
}
public function __get($name) {
if(isset($_SESSION[$this->_namespace]) && array_key_exists($name, $_SESSION[$this->_namespace])) {
return $_SESSION[$this->_namespace][$name];
}
return null;
}
public function __isset($name) {
return isset($_SESSION[$this->_namespace][$name]);
}
public function __unset($name) {
unset($_SESSION[$this->_namespace][$name]);
}
};
Then I have a CurrentUser class representing the current user. The CurrentUser class has a member named _data which is-a Session object. In the CurrentUser class I override the __get and __set methods to use the _data member.
public function __set($name, $value) {
$this->_data->$name = $value;
}
public function __isset($name) {
return isset($this->_data->$name);
}
public function __get($name) {
if(isset($this->_data->$name)) {
return $this->_data->$name;
}
return null;
}
Now to keep track of which threads have been unread, I fetch all threads whose date is >= the user's last_seen date. I also have methods to remove board and threads from the array.
public function buildUnreadList($since) {
// Build a "new since last visit" list
$forumModel = new Model_Forum();
$newThreads = $forumModel->fetchThreadsSinceDate($since);
foreach($newThreads as $thread) {
$tmp =& $this->unreadThreadsList;
$tmp[$thread['board']][$thread['id']] = 1;
}
}
public function removeThreadFromUnreadList($boardid, $threadid) {
$threads =& $this->unreadThreadsList;
unset($threads[$boardid][$threadid]);
}
public function removeBoardFromUnreadList($boardid) {
$threads =& $this->_data->unreadThreadsList;
unset($threads[$boardid]);
}
This is where I'm running into problems. I'm getting a Indirect modification of overloaded property Session::$unreadThreadsList has no effect error on $threads =& $this->_data->unreadThreadsList; How can I either fix this problem or design a better solution? I thought about creating a class that keeps track of the array so I don't have to have an array of arrays of arrays of arrays, but I'm not certain on persisting objects and creating an object just to manage an array feels really dirty to me.
Sorry if I'm a little bit off base; I'm trying to understand how the variables are being used (as their initialization is not shown). So $this->unreadThreadsList is an array where the indices (if value set to 1). Why not set everything directly?
Looking at what you're doing, here is an idea I had. It does the same thing but just does some extra checking on $this->unreadThreadsList and it accesses the variable directly.
Assuming I figured out the array structure properly, this should work.
public function buildUnreadList($since) {
// Build a "new since last visit" list
$forumModel = new Model_Forum;
$newThreads = $forumModel->fetchThreadsSinceDate($since);
foreach($newThread as $thread)
{
// Avoid an error if no list pre-exists
if(is_array($this->unreadThreadsList))
if(array_key_exists($thread['board'],$this->unreadThreadsList))
if(array_key_exists($thread['id'],$this->unreadThreadsList[$thread['board']]))
// Skip this result, already in
if($this->unreadThreadsList[$thread['board']][$thread['id']] == 1) continue;
$this->unreadThreadsList[$thread['board']][$thread['id']] = 1;
}
}
This assumes an array structure like:
array(
1 => array(
'board' => 1,
'id' => 2
),
2 => array(
'board' => 3,
'id' => 1
),
3 => array(
'board' => 7,
'id' => 2
));
for the result of "fetchThreadsSinceData($since)" and an array structure of
array(
1 => array(
2 => 1
),
2=> array(
2 => 1
),
3=> array(
2 => 1
));
for the $this->unreadThreadsList where the first index is the board and the second index is the thread id.
For the other functions why not simply unset them directly as well?
unset($this->unreadThreadsList[$boardid][$threadid]);
unset($this->unreadThreadsList[$boardid]);
Good luck!
Dennis M.