i am making a database abstraction class that binds objects like an ORM. I'm having issue with a particular case, fetching a single row and binding to a class. While the same is working well with fetchAll() i can't figure out why using fetch(PDO::FETCH_CLASS) the object returns null.
if i use PDO::FETCH_LAZY it works, but isn't a correct binding to the passed class.
Here the code.
The Database() class is connects to db using PDO. Products() is a class made of public attributes with same name of tables.
The controller:
public function editProducts($params) {
$products = new Products();
$db = new Database ();
$id = array_keys($params);
$products = $db->findById($products, $id[0]); // auto Bind object fetched=no and POST params?
$this->template = new Template();
$this->template->renderArgs("product", $products);
$this->template->renderArgs("page_title", "Edit product " . $products->title);
$this->template->render(get_class($this), "editProducts");
}
The DB class:
public function findById($object,$id) {
try {
$table = $this->objectInjector($object);
} catch (Exception $e) {
if (APP_DEBUG) {
d($e->getTrace());
}
return;
}
$statement = "SELECT * FROM $table WHERE id=:id";
$this->stm = $this->pdo->prepare($statement);
$this->bindValue(":id",$id);
return $this->fetchSingleObject($object);
}
the method that abstract fetch:
public function fetchSingleObject($object) {
$this->execute();
$this->stm->setFetchMode(PDO::FETCH_CLASS, get_class($object));
return $this->stm->fetch(PDO::FETCH_CLASS);
//return $this->stm->fetch(PDO::FETCH_LAZY); this works!
}
I missed something? the fetchAll() works nicely in this way:
public function fetchObjectSet($object) {
$this->execute();
$this->stm->setFetchMode(PDO::FETCH_CLASS, get_class($object));
return $this->stm->fetchAll(PDO::FETCH_CLASS);
}
Thank you so much.
PS: some methods like $this->execute() are just abastractions to pdo->statment method since pdo and stm are db class instance variables.
I found the answer to the question by myself, i post the answer for everyone.
Instead of using directly PDO::FETCH_CLASS, $Class, i switched using setFetchMode() passing PDO_FETCH_INTO, new $Object instance.
This return correctly new instance of given object (with object methods and fields). Works well with public attributes and overloaded constructors.
The previously statement "findAll() works" wasn't true, i was returning somehow like FETCH_OBJ, an object representation of the database table.
Here the solution:
public function fetchSingleObject($object) {
$this->stm->setFetchMode(PDO::FETCH_INTO, new $object());
$this->execute();
return $this->stm->fetch();
}
Return a new instance of passed in object.
Works also as fetchAll()
EDIT:
public function fetchObjectSet($object) {
$this->execute();
return $this->stm->fetchAll(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, get_class($object));
}
The manual states:
bool PDOStatement::setFetchMode ( int $PDO::FETCH_CLASS , string
$classname , array $ctorargs )
so perhaps try:
$this->stm->setFetchMode(PDO::FETCH_CLASS, 'get_class', $object );/* is $object an array ? */
or, without
$this->stm->setFetchMode(PDO::FETCH_CLASS, 'get_class' );
Related
I'm working on a project today and i've been working for a while now and i don't see what i do wrong here. Can someone give me the right example.
Thnx a lot!
Connector:
class Repository
{
private $connector;
public function __construct(Config $connector)
{
$this->connector = $connector;
}
public function events()
{
$query = 'SELECT * FROM digi_gz_parties';
$dbh_query = $this->connector->getDatabase()->prepare($query);
$dbh_query->execute();
$dbh_querys = $dbh_query->fetchAll();
return $dbh_querys;
}
}
Getter:
class REST
{
public function getEvents()
{
require 'logic/Repository.php';
$event = new Repository();
$events = $event->events();
return $events;
}
}
Error:
Argument 1 passed to Repository::__construct() must be an instance of Config.
I know i need to give a paramater to the repository but i don't want it, i want only to call the repository without give some paramter.
Thanks a lot!
You can change your Repository::__construct() to have a default $connector to null:
public function __construct(Config $connector = null)
{
$this->connector = $connector;
}
That way, if you instanciate your object without any parameter, like you do here, it will default to null. The only downside of that is that, now, you have to be extra careful when using $this->connector inside your Repository class and remember it could be null.
For exemple, here, the second line of your events() method is not going to work, because you lack the proper configuration to connect to your database.
I'm attempting to fetch, convert and save a value in a models' constructor in Laravel 5.2. The reason being that it's saved in the database as hex, and I need to convert it to binary pretty often, and would like to do it once and save the result in a class attribute. But I can't seem to be able to fetch the value from $this in the constructor.
Here's a excerpt of what I'm working with, guid is a field in my table.
class Person extends Model {
private $bGuid = null;
public function __construct(array $attributes = []) {
parent::__construct($attributes);
$this->ad = Adldap::getProvider('default');
$this->bGuid = hex2bin($this->guid);
}
public function getName(){
$query = $this->ad->search()->select('cn')->findBy('objectGUID', $this->bGuid);
return $query['attributes']['cn'][0];
}
}
The $this->ad attribute executes as expected, but $this->bGuid does not. Some debugging shows that $this->guid when referenced in the constructor returns null. While if referenced in the getName() method directly works just fine.
My intermediate solution is creating a new function and just call $this->getbGuid(), thus making me a bit more satisfied with the DRY-ness, but it still has to convert it each time it is called.
I would appreciate it if anyone could tell me what's going wrong, so I could improve the code :)
Try to override another method from Model: newFromBuilder().
This is the one that is executed once the data is retrieved from the DB, not the __construct() one:
class Person extends Model {
private $bGuid = null;
public function newFromBuilder($attributes = [], $connection = null)
{
$model = parent::newFromBuilder($attributes, $connection);
$model->bGuid = hex2bin($model->guid);
return $model;
}
}
Note, that inside the overridden method you refer to the object as $model (instead of $this), and it has to return the $model object at the end.
I want to get the the id of the last inserted row in a database so I created a static field in my model:
public static $lastid;
and I try to override the create method:
public static function create($data){
parent::create($data);
$lastid = DB::getPdo()->lastInsertId();
}
now I have an Error exception saying:
Declaration of Actor::create() should be compatible with Illuminate\Database\Eloquent\Model::create(array $attributes)
how can I make this work?
Here, the keyword array before $data will print a fatal error if the data are not an array.
Like is said in the error, your class extends Illuminate\Database\Eloquent\Model and should be compatible with his parent.
public static function create(array $data = array()){
parent::create($data);
$lastid = DB::getPdo()->lastInsertId();
}
When you use the Model::create method you can grab the ID straight from the result.
$actor = Actor::create(array('name' => 'Jason'));
dd($actor->id);
You shouldn't need to use the lastInsertId method from the PDO object.
Let me know if you have a better title for this question :)
Up till now, I've created my factory class by doing this:
include_once('menu.class.php');
include_once('gallery.class.php');
class Factory {
function new_menu_obj() { return new Menu(Conn::get_conn()); }
function new_gallery_obj($type ='', $id='') { return new Gallery(Conn::get_conn(), $type, $id); }
/* Many more defined functions here */
}
class Conn { // DB connection }
// To create a new class object I just do this
$menu = Factory::new_menu_obj();
$gallery= Factory::new_gallery_obj('some-type','3');
Now I'm trying to do this more dynamically by using this code:
include_once('menu.class.php');
include_once('gallery.class.php');
class Factory {
private $db_conn;
private function __construct() {
$this->db_conn = Conn::get_conn();
}
public function create( $class_name ) {
if ( $this->db_conn === null ) {
$this->db_conn = Conn::get_conn();
}
return new $class_name( $this->db_conn );
}
}
class Conn { // DB connection }
// To create a new class object I just do this
$menu = Factory->create("Menu");
$gallery= Factory->create("Gallery"); // How do I pass more parameters here?
Is this the "correct way" of being efficient? :)
How can I create a new object passing variables when I do not know how many variables needs to be passed? Using a array?
1 - Factory->anything will fail, I guess you mean Factory::something in your code. Anyway, with a private constructor, you won't be able to create any instance of the Factory class outside of some static Factory method...
2 - since you use static methods, use the static keyword in your function declaration should be wise.
3 - as a side and personnal note, I'm not sure why you would want to do that. The first flavour of your class does a real factory job : it creates a consistent set of classes. And you will use another flavour of factory to create another set of similar but still consistent classes.
E.g. the Factory class creates Menu and Gallery instances, the AltFactory class creates AltMenu and AltGallery instances and so on.
But you loose this benefit with the second version of your factory. Just calling the new operator on your classes instead of calling your create construct will give you exactly the same degree of dependencies, so...
Using reflection and adding a $params parameter to your create method:
class Factory {
private $db_conn;
private function __construct() {
$this->db_conn = Conn::get_conn();
}
public function create( $class_name, $params = []) {
if ( $this->db_conn === null ) {
$this->db_conn = Conn::get_conn();
}
array_unshift($params, $this->db_conn);
$reflect = new ReflectionClass($class_name);
return $reflect->newInstanceArgs($params);
}
}
class Conn { // DB connection }
$menu = Factory->create("Menu");
$gallery= Factory->create("Gallery", ['pics' => ... ])
I am wondering whether or not it is possible to elegantly map the results of a PDO query to an array member in a class rather than have them floating about as public properties of that object.
Say I have the (condensed) following:
class DBObject {
protected
$record = array();
function __construct(array $record) {
if(!empty($record)) {
$this->loadRecord($record);
}
}
}
Ideally, I want to call the constructor with an array of values passed from the database, rather than use __set or any other weird methods. So using PDO's existing API would be great.
My rough get_all function at the moment has got this far:
static function get_all() {
$class = get_called_class();
$results = DB::factory()->query('SELECT * FROM ' . $class . ' ORDER BY ID');
$results->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, $class);
return $results;
}
NB: I'm running PHP 5.3 and MySQL through PDO, and already know this problem is solveable using __set, but I explicitly want to avoid using it in favour of something more performant.
You don't need to pass arguments to a constructor to make a class with private members using PDO::FETCH_CLASS. You can do something like this:
<?php
class Songs
{
private $artist;
private $title;
public function __construct()
{
}
public function get_artist()
{
return $this->artist;
}
public function get_title()
{
return $this->title;
}
private function set_artist($artist)
{
$this->artist = $artist;
}
private function set_title($title)
{
$this->title = $title;
}
}
I'm actually doing that on a demo site that I built. It works just fine with PDO::FETCH_CLASS. By default, FETCH_CLASS creates objects by populating the fields BEFORE the constructor. Think of it as bypassing the constructor. And it will do this with private members.
If you'd rather pass arguments to the constructor you can do your query like this:
$obj = $statement->fetchALL(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Songs', $params);
In that case your constructor would look like this:
public function __construct($params)
{
$this->artist = $params[0]['artist'];
$this->title= $params[0]['title'];
}
Removed previous code
Right, can't you do something like this:
class DBObject {
protected $record = array();
function __construct($record = null) {
if(null === $record){
$obj_vars = get_object_vars($this);
$cls_vars = get_class_vars(get_class($this));
$this->$record = array_diff_key($obj_vars, $cls_vars);
}else{
$this->record = $record;
}
}
}
The problem with this however is that the values are still available as public members.
But what it will do is compare 'pre-defined' (class) members to the actual (object) members.
Since PDO will create new members in the object you can use array_diff_key to get the 'new' members.
Yes, this will still not pass them through your constructor.
How about using magic __set() method:
<?php
class MyClass
{
protected $record = array();
function __set($name, $value) {
$this->record[$name] = $value;
}
}
$pdo = new PDO("mysql:host=localhost;dbname=db", 'user', 'password');
$results = $pdo->query('SELECT * FROM table');
$results->setFetchMode(PDO::FETCH_CLASS, 'MyClass');
PHP will call this magic method for every non-existent property passing in its name and value.