I've been unable to successfully retrieve records between two dates in Zend Framework 2.
My code looks as follows:
$select = $this->getTimeTable();
$predicate = new \Zend\Db\Sql\Where();
$select->where($predicate->between("start_date", $week_sunday_date , $satruday_date));
public function getTimeTable()
{
if (!$this->timeTable)
{
$this->timeTable = new TableGateway(
'time',
$this->getServiceLocator()->get('Zend\Db\Adapter\Adapter')
);
}
return $this->timeTable;
}
"start_date" is the column in my DB that is of type DATETIME, and $week_sunday_date , $satruday_date are both generated as follows
$week_sunday_date = date("Y-m-d\TH:i:sP",strtotime('this sunday'));
$satruday_date = date("Y-m-d\TH:i:sP",strtotime("+6 day", strtotime($week_sunday_date)));
The database connection is good and I can otherwise get data. Dumping both $week_sunday_date and $satruday_date variables above I see:
string(25) "2014-11-08T00:00:00+00:00"
string(25) "2014-11-02T00:00:00+00:00"
I've also tried several other ways of in between but ultimately the page is blank and won't load due to an error.
Seems like I need something like this:
$statement = $sql->prepareStatementForSqlObject($select);
$time_list = $statement->execute();
But not sure how to initialize $sql properly.
I think the correct syntax would be:
$select->where->between('start_date', $week_sunday_date, $saturday_date);
Oh, but since, in your case $select is an instance of TableGateway, then you probably need $select->getSql()->where->between(...).
I'm a little rusty on TableGateway, we've strayed away from using it. But I think you can do something like:
$timeTable = new TableGateway(/*...*/);
$select = $timeTable->getSql()->select();
$select->columns(array('col1', 'col2'));
$select->where->between('start_date', $week_sunday_date, $saturday_date);
$resultSet = $timeTable->selectWith($select);
var_dump($resultSet->toArray());
Note: I do not recommend always converting the result set to an array, but it's nice to be able to do this for debugging purposes.
Example not using TableGateway:
Query class:
namespace Example\Model\Sql\Select;
use Zend\Db\Sql\Select;
class Time extends Select {
public function __construct($week_sunday_date, $saturday_date) {
parent::__construct(['t' => 'Time']);
$this->columns([
'col1',
'col2',
]);
$this->where->between('start_date', $week_sunday_date, $saturday_date);
}
}
Data model: (basically a glorified array object, just holds data, no business logic)
namespace Example\Model\DataContainer;
class Time extends \ArrayObject {
protected $col1;
protected $col2;
public function exchangeArray($data) {
$this->col1 = $data['col1'];
$this->col2 = $data['col2'];
}
public function getCol1() {
return $col1;
}
public function getCol2() {
return $col2;
}
}
Controller:
namespace Example\Controller;
use Example\Model\DataContainer;
use Example\Model\Sql\Select;
use Zend\Db\ResultSet\ResultSet;
class IndexController {
public function myExampleAction() {
$request = $this->getRequest();
$sm = $this->getServiceLocator();
$query = new Select\Time($request->getQuery('sunday'), $request->getQuery('saturday'));
$resultSet = new ResultSet(ResultSet::TYPE_ARRAYOBJECT, new DataContainer\Time());
$executer = $sm->get('Executer');
$resultSet = $executer->execute($query, $resultSet);
return [
'time' => $resultSet, // loop through results in your view
];
}
}
So, we create an instance of our query class, which is set up when you call the constructor implicitly by creating a new instance of it. We pass our parameters into it. Then, we create an instance of our result set class, and we specify the array object prototype. Each row returned by our query will be populated as an instance of the array object prototype. When the result set is initialized, it will automatically call exchangeArray() with the data returned for the current row. That method populates your data container, and so the result set is populated with an array of these populated data container objects. So when you loop through the result set, each row will be represented by an instance of your array object prototype.
You will have to define your own Executer class. That's not a built-in. You will have to create it and add it to the service config, so that you can get it out of the service manager like I've done in the example.
A quick example for the $sql property as you requested at the end of your question.
You should use:
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Select;
$adapter = $this->tableGateway->getAdapter();//get connection
$sql = new Sql($adapter);
$select = $sql->select();
$select->from($this->tableGateway->getTable())->where(array('active' => '1'));
$selectString = $sql->getSqlStringForSqlObject($select);
//echo $selectString;die;//this will show the quesry string build above.
$results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);
$resultSet = new ResultSet();
$resultSet->initialize($results);
return $resultSet->toArray();//return array(use $resultSet->current() for one row)
i hope this helps
Related
I'm trying to adapt a class of mine that handles tags for events stored in a JSON file. You can create tags, delete them, restore them, view them, etc. In the code below for this library you can see that I retrieve the array from the file during the constructor function so I use it and manipulate it throughout my classes' functions.
class tagHandler {
private $tagsFile = "/home/thomassm/public_html/functions/php/tags.json";
private $LstTags;
private $LstReturn;
function __construct() {
$this->LstTags = array();
if(!file_exists ($this->tagsFile)){
$fHND = fopen($this->tagsFile, "w");
$tmpArray = array(array("EID","EName","EColor", "EDel"));
fwrite($fHND, json_encode($tmpArray));
fclose($fHND);
}
$encodedInput = file ($this->tagsFile);
$this->LstTags = json_decode($encodedInput[0], true);
if(!$this->LstTags) $this->LstTags = array();
}
function __destruct(){
$this->update();
}
public function update(){
$this->LstTags = array_values($this->LstTags);
$fHND = fopen($this->tagsFile, "w");
fwrite($fHND, json_encode($this->LstTags));
fclose($fHND);
//empty memory region
$this->LstTags = array();
$encodedInput = file ($this->tagsFile);
$this->LstTags = json_decode($encodedInput[0], true);
}
//More functions that use the collected array here.
I am trying to adapt the class to deal with people signed up to my events. Each event has a record in my database that will store a field for an array of males who sign up and females who sign up. I wish for the constructor class to get the arrays(s) from the record so they can be manipulated like the previous class. The issue is to get the array I have to search the DB for a record with the Event ID (EID) and that will require a variable passed to the constructor function. To make things worse, this parameter has to be able to change in a loop. For example, the page listing all the events will have to use this class in a loop going through each record, so it can retrieve the array to manipulate it and then show it in a table / fullcalendar before repeating the process to get the next event. I have put the code I have so far below. Its not complete (some variables haven't been renamed to male and female, etc) and may be completely wrong, but it will give you a base to explain from.
class signupHandler {
private $LstMaleS;
private $LstFemaleS;
private $LstReturn;
function __construct($IntEID) {
$this->LstTags = array();
$StrQuery = "SELECT MaleS, FemaleS FROM tblEvents WHERE EID = ?";
if ($statement = TF_Core::$MySQLi->DB->prepare($StrQuery)) {
$statement->bind_param('s',$IntEID);
$statement->execute ();
$results = $statement->get_result ();
}
$this->LstTags = json_decode($encodedInput[0], true);
if(!$this->LstTags) $this->LstTags = array();
}
Thanks,
Tom
function decodeNames($StrNames){
$this->LstNames = array();
$this->LstNames = json_decode($StrNames, true);
if(!$this->LstNames) $this->LstNames = array();
$this->LstNames = array_values($this->LstNames);
}
function update(){
$this->LstNames = array_values($this->LstNames);
return json_encode($this->LstNames);
}
public function addSignUp($StrNames, $StrUsername, $StrStatus){
$this->decodeNames($StrNames);
$BlnPresent = false;
for($i = 0; $i < count($this->LstNames); $i++){
if($this->LstNames[$i][0] == $StrUsername){
$this->LstNames[$i][1] = $StrStatus;
$BlnPresent = true;
}
}
if($BlnPresent == false){
array_push($this->LstNames, array($StrUsername, $StrStatus, date("Y-m-d H:i:s")));
}
return $this->update();
}
I have decided to pass the encoded JSON array to the class each time I call a function from it. Before every function it is decoded and turned into an array and at the end it is then re-encoded and returned back to the file calling it. Now I no longer have any constructor or destruct functions.
I'm using $casts to save data in array to database. I have an issue with that.
How can i push data to an existing array in the database?
For example i have already an array of data in my db column like: ["some_data", "another_el"] and so on and in the Controller i want to push in this array in db some other data from input.
$brand = Brand::find($request->input('brand'));
$brand->model = $request->input('model');
$brand->update();
Pushing data like this.
You cannot do this with Eloquent's Mass Assignment functions (update, create, etc). You must pull down your field, change it, then save the model.
$collection = collect($brand->field);
$collection->push($myNewData);
$brand->field = $collection->toJson();
$brand->save();
Way 1
$brand = Brand::find($request->input('brand'));
$brand->model = array_merge($brand->model, [$request->input('model')]);
$brand->update();
Way 2 (my favorite because it encapsulates the logic)
$brand = Brand::find($request->input('brand'));
$brand->addModel($request->input('model'));
$brand->update();
And on Entity:
public function addModel($value)
{
$this->model = array_merge($this->model, [$value]);
}
Optional
And on Entity (instead $casts):
public function setModelAttribute($value)
{
$this->attributes['model'] = json_encode($value);
}
public function getModelAttribute($value)
{
return json_decode($value, true);
}
I need to know album table max id in controller action. so i write following method in AlbumTable Class. When i call this method in action then it return an empty album model object. I guess that it for $resultSetPrototype->setArrayObjectPrototype(new \Album\Model\Album()); prototype setting in service manager. then i disable $resultSetPrototype it return max id as array. Now i am in problem, i can't disable $resultSetPrototype since it is using for data storing and retrieving data in album application and i also required max id for other manipulation. .
How can i get max id considering tablegateway dependency injection.
Here is my method
public function getMaxId() {
$select = $this->tableGateway->getSql()->select();
$select->columns(array(
'maxId' => new Expression('MAX(id)')
));
$rowset = $this->tableGateway->selectWith($select);
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not retrieve max Id of personal table");
}
return $row;
}
I too tried it from my end and the method getMaxId() works.
Try this -
It empty because you must have a property by the name maxIdin the Album Model Class file.
In Album\Model\Album Class file -
class Album {
public $id;
.....
public $maxId;
....
public function exchangeArray($data) {
$this->id = (!empty($data['id'])) ? $data['id'] : null;
.....
$this->maxId = (!empty($data['maxId'])) ? $data['maxId'] : 0;
....
}
}
This way the $row that is returned by your getMaxId() method will have a key by the name maxId.
Eg:
Album\Model\Album Object
(
....
[maxId] => _SOME_VALUE
....
)
I hope it helps.
i had the same problem. what you can do to not use the array object prototype for your select is:
$query = $this->tableGateway->getSql()->prepareStatementForSqlObject($select);
$rowset = $query->execute();
...
that worked for me
I have a PHP class which is supposed to return an array to where the object is instantiated. I have tried to figure out what the problem is but I can't seem to see it.
Can anyone see where I have gone wrong here or point me in the right direction?
Thanks
Here is the class (within a file called 'feeds.php.)
class updateFeed {
public function index() {
$db = JFactory::getDBO();
$query = "SELECT * FROM tweets";
$db->setQuery($query);
$result = $db->loadObject()
if(strtotime($result->date_added) <= time() - 3200) {
$this->updateTweets();
}
$query = "SELECT * FROM tweets ORDER BY tweet_date DESC LIMIT 0, 3";
$db->setQuery($query);
$results = $db->loadObjectList();
$tweet_db = array();
foreach($results as $result) {
$tweet_db[] = array(
'title' => $result->title,
'text' => $result->text,
'tweet_date' => $result->tweet_date
);
}
return $tweet_db;
}
And here is where the object is instantiated (in the index.php file):
include('feeds.php');
$tweet_dbs = new updateFeed;
print_r($tweet_dbs);
The print_r in the index file shows 'updateFeed Object ( ) '.
Thanks in advance.
You are using your class wrong. You do not call index() method.
Try this:
include('feeds.php');
// Create class instance
$instance = new updateFeed;
// Call your class instance's method
$tweet_dbs = $instance->index();
// Check result and have fun
print_r($tweet_dbs);
include('feeds.php');
$tweet_dbs = new updateFeed;
$tweets = $tweet_dbs->index();
print_r($tweets);
You missed to call the index() function..
you need to call that method using object of the class.
replace
print_r($tweet_dbs)
with
print_r($tweet_dbs->index())
PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x