The code:
final class SimpleEventManager {
private $listeners = array();
public function listen($event, $callable) {
$this->listeners[$event][] = $callable;
}
public function fire($event, array $arguments = array()) {
foreach ($this->listeners[$event] as $listener) {
call_user_func_array($listener, $arguments);
}
}
}
$manager = new SimpleEventManager;
$manager->listen('sql', function($sql) {
$sql .= " order by username desc";
});
$sql = "select * from users";
$manager->fire('sql', array($sql));
var_dump($sql); // is: select * from users
// want: select * from users order by username desc
So basically i want my event listeners to be able to modify the arguments that come in. I've tried doing things like array &$arguments = array() but then I'm getting the Cannot pass parameter 2 by reference error.
Does anyone know how I can solve this?
You can't pass it by reference because only variable may be passed by reference. The literal array($sql) is clearly not a variable.
That said, this isn't the problem.
In fact, there's a lot of problems, mostly because of $sql being "copied" so many times:
When creating the array($sql)
When calling fire() (due to not being passed by reference)
When calling the anonymous function (again, not being passed by reference)
First of all you need to define your array as a variable, such as $arr = array(&$sql);
Then keep your current "fix" of passing &$arguments by reference.
Finally, adjust your anonymous function to function(&$sql) to also work by reference.
All in all, this could be made a lot easier if your code weren't so convoluted ;)
Related
Is it possible to store an array as an object property in PHP?
I am building an article class that pulls various information about a research article and stores them as a properties in an object. Since the number of authors vary per research article, I would like to store them as an array in an $authors property, rather than store each author as a separate property. In this code sample, I realize this problem results from working with a poorly designed table, but nonetheless, I would like to see how this code could be used to store an array as an object property.
<?php
Class Article {
public $id;
public $authors;
public $article_name;
public $journal;
public $volume_number;
public $issue_number;
public $article_location;
public function __construct($id, array $authors, $article_name, $journal,
$volume_number, $issue_number, $article_location)
{
$this->$id = $id;
$this->$authors = $authors;
$this->$article_name = $article_name;
$this->$journal = $journal;
$this->$volume_number = $volume_number;
$this->$issue_number = $issue_number;
$this->$article_location = $article_location;
}
}
//function to pull Article information from Articles Table
function getArticle($id){
try {
$query = "SELECT * FROM articles WHERE ID = :ID";
$db = Db::getInstance();
$results = $db->prepare($query);
$results->execute([':ID'=>$id]);
$row = $results->fetch();
$authors = array();
if(!empty($row['author'])){
$authors[] = $row['author'];
}
if(!empty($row['author2'])){
$authors[] = $row['author2'];
}
if(!empty($row['author3'])){
$authors[] = $row['author3'];
}
//This repeats for a while.
return new article($row['ID'],
$authorList,
$row['article_name'],
$row['journals'],
$row['volume_number'],
$row['issue_number'],
$row['article_location']);
} catch (PDOException $e) {
return "Unable to pull articles from the Articles table.";
echo $e->getMessage();
}
}
Yes, it is possible to store an array as a property.
The problem is that you use properties wrong.
$this->$authorList
Is wrong, you should use:
$this->authorList
Your code currently creates properties for your class based on the original property's value - if $article_name has the value of 'ABCD', $this->$article_name creates and fills the property 'ABCD' - being the equivalent of $this->ABCD = $article_name;, meaning you won't be able to access the value in the original property. It's the same with $this->$authors = $authors; - if you are passing an array as $authors, your code will try to store it as a string, making the situation even worse. Removing the $ before $authors solves this issue too.
Also, when you use $authorList[], you are pushing values into a local variable, not into the class property. It's not necessarily the wrong way to do it, as long as you copy the local variable's content into the property, but I would strongly suggest not to use variables named after properties. It makes your code harder to maintain, as it can confuse developers.
In my controller, I can get the organization name but when I pass it to the view
there's an error. It said invalid argument supplied for foreach( ):
.
This is my codes.
Controller
public function index()
{
$user_id = $this->session->userdata('user_id');
$data['title'] = "User";
$getID['orgID'] = $this->userModel->getOrganizationID($user_id); // used my session user_id to
foreach ($getID['orgID'] as $orgID)
{
$org_id = $orgID->org_id;
$getName['myOrganization'] = $this->userModel->myOrganization($org_id);
foreach($getName['myOrganization'] as $orgName)
{
$name = $orgName->org_name;
$data['name'] = $name;
}
}
$this->load->view('xxxx/xxxx/xxxx',$data);
Model
public function getOrganizationID($user_id)
{
$this->db->select('org_id');
$this->db->from('organization_members');
$this->db->where('user_id', $user_id);
$query = $this->db->get();
return $query->result();
}
public function myOrganization($org_id)
{
$this->db->select('org_name');
$this->db->from('tblorganization');
$this->db->where('org_id', $org_id);
$query = $this->db->get();
return $query->result();
}
My output
First array is my result of $getID['orgID'] = $this->userModel->getOrganizationID($user_id); which I used my user_id session to get all the org_id of the user then
Second array is my result of $getName['myOrganization'] = $this->userModel->myOrganization($org_id); which I used my org_id(from my previous method) to get all the org_name of the user.
Is there going to be more then one result? Because if its only one result then you can use $query->row(); and eliminate the foreach completely.
Always check to make sure your database method worked AND that you actually got a returned value whenever you are making any database call. So i'll let you add the if condition in the database method but in short it should return FALSE if nothing came back. So thats the database method heres one way of doing it in your controller. Note this: $getID['orgID'] is very awkward. You are getting results back from the members table so call it members.
// check for the negative first - if no members came back
if( ! $members = $this->userModel->getOrganizationID($user_id) )
{
// if no results back leave this method
// pass the user id so you can echo it out in the error page
$this->showNoResultsFor($user_id) ;
}
else{
foreach ($members as $member)
{
$org_id = $member->org_id;
// etc etc etc
I'm not a codeigniter expert but looking at your code, I am wondering why you are setting:
$getID['orgID'] = $this->userModel->getOrganizationID($user_id);
First, you are setting an array $getID['orgID'] rather than just using something like $memberships = ...; I'm not sure why you are casting an array.
Secondly, you seem to be referencing a model class without instantiating it:
$this->userModel->getOrganizationID($user_id);
Perhaps codeigniter does some magic? $this refers to this instance and from the code you show, your model is likely in a separate class/file so I am unclear how $this->userModel is referenced in your method, unless you are instantiating it in your Controller's constructor?
From what I see it looks like you are getting the error because you are not supplying a valid object/array to your foreach. Perhaps start by testing you are actually getting a valid return from $this->userModel->getOrganizationID($user_id).
Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}
Lets say I have a class like this:
Class User {
var $id
var $name;
}
And I run a query using PDO in php like so:
$stm = $db->prepare('select * from users where id = :id');
$r = $stm->execute(array(':id' => $id));
$user = $r->fetchObject('User');
If I vardump my user object it has all kinds of other fields in it that I have not defined in the User class. Obviously I could make my query specific so that it only gives me back the fields I need/want. But if I don't want to do that is there any way to make this work the way I want it to?
I like the idea of fetchObject, because it's one line of code to create this object and set member variables for me. I just don't want it to set variables I haven't defined in my class.
EDIT:
Well it seems like karim79 is right and the fetch or fetchObject won't work the way I want it to. I've added the following bit of code after I do the fetch to get the desired results.
$valid_vars = get_class_vars('User');
foreach (get_object_vars($user) as $key => $value) {
if (!array_key_exists($key, $valid_vars)) {
unset($user->$key);
}
}
Obviously not the most elegant solution :/ I'm going to extend the PDOStatement class and add my own method fetchIntoObject or something like that and automatically do these unsets. Hopefully shouldn't be to much overhead, but I want to be able to easily fetch into an object with 1 line of code :)
SUPER EDIT:
Thanks to mamaar's comment I went back to the documentation again. I found what the problem is. http://us.php.net/manual/en/pdo.constants.php and scroll down to PDO::FETCH_CLASS and it explains that the magic method __set() is used if properties don't exist in the class. I overwrote the method in my target class and tada, works. Again, not the most elegant solution. But now I understand the WHY, and that's important to me :D
PDOStatement->execute() does not return an object - it returns TRUE/FALSE.
Change lines 2 and 3 to
if ( $stm->execute( array( ':id' => $id ) ) ){
$user = $stm->fetchObject( 'User' );
}
and it works
I don't think that's possible. fetchObject will create an instance of the classname specified as fetchObject's $class_name parameter (which defaults to stdClass). It will not check for existing classes with the same name and create an instance, assigning values only to member variables which match column names in the result. I would suggest relying on something more boring, like this:
$user = new User($result['id'], $result['name']);
Which would of course mean giving your User class a constructor:
Class User {
var $id
var $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
You could probably use the PDOStatement->fetch method with PDO::FETCH_CLASS or PDO::FETCH_INTO as the $fetch_style parameter
Edit: So I've tried myself, and got it to work with PDOStatement->setFetchMode
class User
{
public $id;
public $name;
}
$db = new PDO('mysql:host=127.0.0.1;dbname=test', 'username', 'password');
$stmt = $db->prepare("select * from users where id=:userId");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$stmt->execute(array(':userId' => 1));
$user = $stmt->fetch();
var_dump($user);
As alternative, you can of course just fetch an array and simply typecast this yourself:
$user = (User) $r->fetch();
Btw, I've not seen this behaviour. Maybe you have PDO::FETCH_LAZY activated, that might create extra data. You could test it with ->fetchObject("stdClass"), else the reason resides with your User class, or its Parent?
Currently I'm programming a database class which makes a little bit use of PHP's PDO class, but I'd like to add some simple features for making programming a certain application a bit easier.
Now In the following piece of pseudo code you can see where I'm going. The only problem in this example is that the $result variable is an object, which cannot be used for comparisation of some stuff I'm doing further on in the script:
<?php
class Database
{
public function FetchRow ( $query )
{
// .. do some stuff, and make a $result variable
return DatabaseStatement ( $result );
}
}
class DatabaseStatement
{
private $result;
public function __construct ( $query )
{
// .. save result in property etc.
}
public function __get ( $column )
{
// .. check result item
return $this -> result [ $column ];
}
}
$db = new Database;
$result = $db -> Query ( 'SELECT * FROM users WHERE id = 1;' );
if ( $result != null ) // Here $result should be an array OR null in case no rows are returned
{
echo $result -> username; // Here $result should call the __get method
echo '<pre>' , print_r ( $result ) , '</pre>'; // Here $result should be the array, cause it wasn't null just yet
}
As you can see the $result variable should not be an object when I'm doing a comparisation, I know it can be made to a string using __toString. But I'd like it to be some other type, mostly an array or null.
How do I get something like that working if it's possible (should be possible I think with too much hassle)?
So can somebody point me in the right direction, or possibly give a piece of code that should work or I can change to fit in my current class?
It seems to me that you just need to add some methods that do what you want. Instead of forcing the $result object to be an array or null to check whether it's empty, why don't you just create and call a method isEmpty () that tells you what you want to know?
And if you need an array, create a method toArray () that returns what you want. OR, even better, make your object implement Iterator and/or ArrayAccess from the Standard PHP Library.
I think you'll have to do this in the same place you create the DatabaseStatement. So for instance:
public function FetchRow($query)
{
// ... do some stuff, and make a $result variable.
$ds = DatabaseStatement($result);
if ($ds) {
return $ds;
}
else {
return null;
}
}
That's not possible. PHP doesn't allow you to overload operators.
Use the PDOStatment class and it's rowCount property.