Postgres JSONB support on zend/Db Zend Framework 3? - php

how can i work with JSONB (postgresql) in zend framework 3?.
for example i want make query's like this
SELECT id, data->'title' AS title, data->'description' as description FROM tablename
SELECT jsonb_array_elements_text(data->'tags') as tag FROM tablename WHERE id = 1;
Or EXECUTE a function like a
SELECT myFunction()
jsonb_each(jsonb)
INSERT INTO tablename VALUES (1,'{"title": "title 1","category":"category 2"}')
UPDATE tablename SET data = jsonb_set(data,'{"image","quality"}','{" copyright":"company X","registered":true}') where id=2;
ETC.
I have a model and only get the id value
<?php
namespace EntityModel\Model;
class Entity
{
public $idEntity;
public $title;
public $description;
public $access;
public $category;
public $isVisible;
public $urlProyect;
public $urlDownload;
public $pedagogicUse;
public $PublicatorUser;
/**
* [exchangeArray description]
* #param array $data [description]
* #return [type] [description]
*/
public function exchangeArray(array $data)
{
$this->id = !empty($data['id']) ? $data['id'] : null;
$this->title = !empty($data['title']) ? $data['title'] : null;
$this->description = !empty($data['description']) ? $data['description'] : null;
$this->access = !empty($data['access']) ? $data['access'] : null;
$this->category = !empty($data['category']) ? $data['category'] : null;
$this->isVisible = !empty($data['isvisible']) ? $data['isvisible'] : null;
$this->urlProyect = !empty($data['urlproyect']) ? $data['urlproyect'] : null;
$this->urlDownload = !empty($data['urldownload']) ? $data['urldownload'] : null;
$this->PedagogicUse = !empty($data['pedagogicUse']) ? $data['pedagogicUse'] : null;
$this->publicatorUser = !empty($data['publicatoruser']) ? $data['publicatoruser'] : null;
}
}
I want to return the jsonb object in the exchangeArray method like id=>4,title=>"title"
How can i implement this ideas, i follow the tutotial from ZF3 and i don't know how implement, maybe here is not the place for this question, but i appreciate if you give me a feedback, i don´t want to use ORM for this project. Thanks
This is the model file we can make a query with this methods
class EntityTable
{
protected $tableGateway;
protected $dbAdapter;
public function __construct(TableGatewayInterface $tableGateway)
{
$this->tableGateway = $tableGateway;
$this->dbAdapter = $tableGateway->adapter;
}
public function getAllFromQuery()
{
$query = $this->dbAdapter->query("SELECT count(*) FROM tablename WHERE data ? 'isVisible';", Adapter::QUERY_MODE_EXECUTE);
$data = $query->toArray();
return $data;
}
it´s important than you have declare in your db adapter
'db' => [
'driver' => 'Pgsql', // pdo not support all the pg_ functions
'database' => 'database',
'username' => 'user',
'password' => 'pass'
]
In a Action call
public function indexAction()
{
return new ViewModel([
'data' =>$this->table->getAllFromQuery()
]);
}
and the view
<?= var_dump($this->data) ?>
AND THE RESULT IS THAT
array(1) { [0]=> array(1) { ["count"]=> string(1) "6" } }
Well this is a want to take from the data base but, how can i pass the data array into a entity exchangeArray method??
i get it!!
public function getAllFromQuery()
{
$query = $this->dbAdapter->createStatement("SELECT id,
data->>'title' as title,
data->>'acess' as access,
data->>'category' as category
FROM tablename");
$data= $query->execute();
/* OR if you want execute a function from postgres */
//$query = $this->dbAdapter->query("SELECT totalregistrosdinamico('tablename')", Adapter::QUERY_MODE_EXECUTE);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->initialize($data);
$resultSetPrototype->setArrayObjectPrototype(new Model\EntityName());
return $resultSetPrototype;
}

Related

Public __construct , retrieve user information class

I am looking to create a class and retrieve the account information in private and then make a public Getters
do you have any advice to improve this code?
private $db;
private $get;
public function __construct($db = null)
{
$this->db = new Database();
}
private function get($information = 0, $userid = null)
{
if ($userid === null)
{
//if ($this->isOnline()) {
$token = $_COOKIE['session'];
$req = $this->db->query('SELECT user_id FROM cms_sessions WHERE token = :token',
array(
"token" => $token
));
$userid = $req[0]['user_id'];
//}
}
$req = $this->db->query('SELECT '. $information .' FROM users INNER JOIN users_info ON users.id = users_info.user_id WHERE users.id = :userid',
array(
"userid" => $userid
));
return (count($req) > 0) ? htmlspecialchars($req[0][$information]) : "Erreur";
}
public function getId()
{
$req = $this->db->query('SELECT user_id FROM cms_sessions WHERE token = :token',
array(
"token" => #$_COOKIE['session']
));
return (count($req) > 0) ? intval($req[0]['user_id']) : "0";
}
public function getIP()
{
return $this->get('last_ip');
}
public function getGender()
{
return $this->get('gender');
}
}
but this gives me '0' once I try to retrieve user information
Thank you for your response! cordially
If you want to follow SOLID design principles to have decoupled code, then here is another way of achieving the required results
Lets define a contract which is responsible for returning a user
interface Extractable
{
public function user($db=null, $userId=null);
}
Lets define a User extractor class to get the user from database or session and will return the user to us
class UserExtractor implements Extractable
{
public function user($db=null, $userId=null)
{
return $this->retrieve($db,$userId);
}
protected function retrieve($db, $userId)
{
$db = $db ?? new Database();
// This logic can further be extracted to its own method to get rid of
// ugly conditional
if($userid ) {
$user = $db->query('SELECT * FROM users INNER JOIN users_info ON users.id = users_info.user_id WHERE users.id = :userid',
array(
"userid" => $userid
));
} else {
// I believe you have this method defined so you can
// bring it in this class
if($this->existValue('session')) {
$user = $this->getValue('session');
}
}
return $user;
}
}
Now we can define the User class which will depend on contract and will give us the required info
class Users extends Session
{
protected $extractable;
// Ok lets work with interface and not the concrete class
public function __construct(Extractable $extractable)
{
$this->extractable = $extractable
}
public function getId()
{
return (int) $this->extractable->user['id'];
}
public function getTokenTimes()
{
return (int) $this->extractable->user['token_times'];
}
}
You can now use them as follow;
$extractedUser = new UserExtractor($db, 5); // whatever the params are
$user = new Users($extractedUser);
$user->getId();
$user->getTokenTimes();
Welcome to stackoverflow!
"Improvement" is a really subjective topic. If I understand your code correctly then I would personally decouple the logic a little bit. In your concrete case I would use a Repository which accesses the database and return a Model and which will have your public getters.
class User
{
private $attributes = [];
public function __construct(array $attributes)
{
$this->attributes = $attributes;
}
public function getId()
{
return $this->attributes['id'];
}
public function getIP()
{
return $this->attributes['last_ip'];
}
public function getGender()
{
return $this->attributes['gender'];
}
}
class UserRepository
{
private $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getById($id)
{
$result = $this->db->query('SELECT * FROM ... WHERE userid = :userid', ['userid' => $id]);
return new User($result);
}
public function getByToken($token)
{
$result = $this->db->query('SELECT * FROM ... WHERE token = :token', ['token' => $token]);
return new User($result);
}
}
And finally you could use it like this:
$repository = new UserRepository(new Database());
if (!empty($_COOKIE['session'])) {
$user = $repository->getByToken($_COOKIE['session']);
} else if ($userid > 0) {
$user = $repository->getById($userid);
} else {
// Pseudo exit here. You should handle this accordingly.
exit('User does not exist / not logged in');
}
echo $user->getId();
echo $user->getIP();
echo $user->getGender();

zf2 tablegateway select columns with join table

I have this code (which works fine):
Class SsoRequserTable
public function fetchAll()
{
$select = new Select();
$select->from("sso_requser");
$select->join('sso_workunit', 'sso_workunit.sso_workunit_id = sso_requser.sso_workunit_id', array("unitname"), 'left');
echo $select->getSqlString();
$result = $this->tableGateway->selectWith($select);
return $result;
}
class SsoRequser
class SsoRequser{
public $sso_requser_id;
public $firstname;
public $lastname;
public $fullname;
public $sso_workunit_id;
public function exchangeArray($data){
$this->sso_requser_id = (!empty($data['sso_requser_id'])) ? $data['sso_requser_id'] : null;
$this->firstname = (!empty($data['firstname'])) ? $data['firstname'] : null;
$this->lastname = (!empty($data['lastname'])) ? $data['lastname'] : null;
$this->fullname = (!empty($data['fullname'])) ? $data['fullname'] : null;
$this->sso_workunit_id = (!empty($data['sso_workunit_id'])) ? $data['sso_workunit_id'] : null;
}
}
Module.php
public function getServiceConfig() {
return array(
'invokables' => array(
// 'Aclplugin' => 'Auth\Controller\Plugin\Aclplugin',
),
'factories' => array('SSO\Model\SsoRequserTable' => function($sm) {
$tableGateway = $sm->get('SsoRequserTableGateway');
$table = new SsoRequserTable($tableGateway);
return $table;
},
'SsoRequserTableGateway' => function ($sm) {
$dbAdapter = $sm->get('postgresql_adapter');
$resultSetPrototype = new HydratingResultSet();
$resultSetPrototype->setObjectPrototype(new SsoRequser());
return new TableGateway('sso_requser', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
But I can`t get the sso_workunit columns to work. I have read the Zend Documentation and nothing effectively worked for me. I need name to have an alias "unitname".
And i got the message error "Undefined property: SSO\Model\Entity\SsoRequser::$unitname "
Whether I should add unitname in class SsoRequser ?
Anyone can help me ?

Use a closure for the PDO FETCH instructions

It occurs to me that I want to give my PDO result the instructions on how to instantiate itself into an object when it gets iterated later on, but I do not want to perform the logic until that time.
I've been looking for the existence of this PDO functionality but I cannot find it. What I want to do is essentially this:
public function getUsers()
{
$sql = 'select first_name, last_name, phone, address from users';
return $this->pdo->query($sql, PDO::FETCH_ASSOC, function($row) {
$user = new User();
$user->setName($row['first_name'] . ' ' .$row['last_name'])
->setPhoneNumber($row['phone'])
->setMailingAddress($row['address']);
return $user;
});
}
Is there a good way of accomplishing this with PHP and specifically PDO? Looping through the iterator is not an acceptable answer. I only want to iterate on this recordset once during execution.
You can use a Generator for this via yield. Only when the internal pointer is on certain iteration, the concrete object will be yielded.
class User {
public $picture;
public function __construct($pic) {
$this->picture = $pic;
}
}
function getUsers() {
$pdo = new PDO('mysql:host=localhost;dbname=users', 'root', '');
$query = "SELECT * FROM votesusers";
$res = $pdo->query($query);
while ($row = $res->fetch()) {
yield new User($row['picture_id']);
}
}
foreach (getUsers() as $user) {
var_Dump($user);
}
Output:
object(User)[5]
public 'picture' => string '2' (length=1)
object(User)[6]
public 'picture' => string '9' (length=1)
object(User)[5]
public 'picture' => string '6' (length=1)
object(User)[6]
public 'picture' => string '1' (length=1)
You can create a custom lazy-loading collection that implements the Iterator interface. Here's an example:
class LazyCollection implements Iterator
{
private $stmt;
private $callback;
public function __construct(PDOStatement $stmt, Closure $callback)
{
$this->stmt = $stmt;
$this->callback = $callback;
}
public function current()
{
$callback = $this->callback; // required because PHP is silly
return $callback($this->stmt->fetch());
}
/* rest of interface implementation */
}
And you would use it like this:
$stmt = $this->pdo->query($sql, PDO::FETCH_ASSOC)
$result = new LazyCollection($stmt, function($row) {
return new User($row['name'], $row['phone'], $row['address']);
});
foreach($result as $user)
{
// $user is an instance of User
}
Probably something like this:
$result = $this->pdo->query($sql, PDO::FETCH_CLASS, 'User');
return $result->fetch();

Doing inserts with zend 2's tableGateway

I using zf2's tableGateway and I'm unsure of the design it leads to.
Here is the canonical example of how to use zf2's tableGateway to do an insert (this from the docs):
public function saveAlbum(Album $album)
{
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
But defining the $data array seems redundant because I already have an Album class that looks like this:
class Album
{
public $id;
public $artist;
public $title;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->artist = (isset($data['artist'])) ? $data['artist'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
}
}
In my own project I have a model with about 25 properties (a table with 25 columns). It seems redundant to have to define the class with 25 properties and than also write a $data array inside of the method of a class implementing tableGateway with an element for every one of those properites. Am I missing something?
Another way is to use RowGateway http://framework.zend.com/manual/2.3/en/modules/zend.db.row-gateway.html
Briefly, I'd extend album class from \Zend\Db\RowGateway\AbstractRowGateway class.
<?php
namespace Module\Model;
use Zend\Db\RowGateway\AbstractRowGateway;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
class Album extends AbstractRowGateway
{
protected $primaryKeyColumn = array( 'id' );
protected $table = 'album';
public function __construct( Adapter $adapter )
{
$this->sql = new Sql( $adapter, $this->table );
$this->initialize();
}
}
And then you can do like this
$album->title = "Some title";
$album->save();
Or
$album->populate( $dataArray )->save();
You may want to take a look at my QuickStart 101 Tutorial.
Basically you could do:
saveAlbum(Album $albumObject)
{
$hydrator = new ClassMethods(false);
$albumArray = $hydrator->extract($albumObject);
// v-- not too sure if that one-liner works; normal if() in case it doesn't
isset($albumArray['id']) ? unset($albumArray['id']) :;
// insert into tablegateway
}

PHP Class auto run method

I don't know if the title is really correct for what I'm asking, but here it goes. I'm trying to build a very basic pagination class, just for retrieving a range of items.
This is my class so far
<?php
Class Pagination {
private $_dbh;
private $_currentPage;
private $_totalPages;
private $_startLimit;
private $_totalItems;
private $_table = 'products';
private $_perPage = 8;
private $_allowedTables = ['products', 'cart', 'orders'];
public $_results = [];
public function __construct($dbh = null) {
$this->_dbh = ($dbh !== null) ? $dbh : null;
}
public function getResults() {
$this->_results = $this->_dbh->query(" SELECT * FROM $this->_table ORDER BY id DESC LIMIT $this->_startLimit, $this->_perPage ")->fetchAll();
}
public function setCurrentPage($currentPage = null) {
$this->_currentPage = ($currentPage !== null && is_int($currentPage)) ? $currentPage : 1;
}
public function setPerPage($perPage = null) {
$this->_perPage = ($perPage !== null && is_int($perPage)) ? $perPage : $this->_perPage;
}
public function setTable($table = null) {
$this->_table = ($table !== null && in_array($table, $this->_allowedTables)) ? $table : $this->_table;
}
private function totalItems() {
$this->_totalItems = $this->_dbh->query(" SELECT COUNT(id) AS total FROM $this->_table")->fetch()->total;
}
private function totalPages() {
$this->_totalPages = ceil($this->_totalItems / $this->_perPage);
}
private function startLimit() {
$this->_startLimit = ( $this->_currentPage - 1 ) * $this->_perPage;
}
public function getInfo() {
return array(
'table' => $this->_table,
'perPage' => $this->_perPage,
'currentPage' => $this->_currentPage,
'totalItems' => $this->_totalItems,
'totalPages' => $this->_totalPages,
'startLimit' => $this->_startLimit,
'query' => " SELECT * FROM $this->_table ORDER BY id DESC LIMIT $this->_startLimit, $this->_perPage "
);
}
}
?>
It is incomplete, but this is how I would like to call it
$pagination = new Pagination($dbh); // $dbh = PDO connection
$pagination->setCurrentPage(2);
$pagination->setTable('products'); // optional
$pagination->setPerPage(12); // optional
$products = $pagination->_results();
The problem is that all the variables that I define at the top of my class ( $_currentPage, $_totalPages, $_startLimit and $_totalItems ) are empty.
Even when I set them, they are still empty(as I expect) so I cannot calculate the $_totalPages, $_startLimit, $_totalItems or call the method getResults() which will query the DB for the items.
The first approach was to do everything in the __construct method and it worked, but I feel like it's not good to perform too many actions inside one method.
So my question is how can I modify my class so I can use it as I showed even without calling the setter methods ?
Plus, all the methods that calculate the number of pages, total items, etc.., would have to be called automatically from inside the class so they actually set the variables values, the way it is now they are only available but aren't run anywhere.
Thank you.

Categories