php define class within class (or next best thing) - php

I am aware that a class cannot be defined within a class in php, however I'm curious if there's another way to achieve the desired effect.
I currently have a set of 3 objects used to conduct a search. The first is called $search_request. It contains properties like $keywords (string), $search_results_per_page (int), $page_requested (int), $owner_id (int)
I also have an object called $search_result, it contains properties like $total_matches (int), $result_set (array of objects)
Finally I have the $search_handler object which contains the $search_request and $search_result, along with functions that build the $search_result based on the $search_request.
Usage goes like so:
$search_handler = new search_handler();
$search_handler->search_request->keywords = "cats, dogs";
$search_handler->search_request->search_results_per_page = 10;
$search_handler->search_request->page_search_requested = 1;
$search_handler->get_search_result();
echo $search_handler->search_result->total_matches;
foreach($search_handler->search_result->result_set)
{
//do something
}
All of this works fine. The problem is I want to repeat this model for different objects, so currently I'm forced to use the hackey solution of the "search_" prefix on each class.
I'd like to have something like:
class search
{
public class request
{
$keywords = "";
$search_results_per_page = 5;
$page_requested = 1;
}
public class result
{
$total_matches = null;
$result_set = array();
}
public get_results()
{
//check cache first
$cached = look_in_cache(md5(serialize($this->request)));
if($cached)
{
$this->result->result_set = $cached;
$count = count($cached);
$this->result->total_matches = $count;
}
else
{
//look in db
$results = get_results_from_database($this->request->keywords); //db call goes here
$this->result->result_set = $results;
$count = count($results);
$this->result->total_matches = $count;
}
}
}
//usage
$search = new search();
$search->request->keywords = "cats, dogs";
$search->request->search_results_per_page = 10;
$search->request->page_search_requested = 1;
$search->get_results();
echo $search->results->total_matches;
foreach($search->results->result_set as $result)
{
//do something
}

If for whatever reason you don't want to have those classes you need in different files you can do the following
<?php
class search
{
var $request;
var $result;
public function __construct()
{
$this->request = new StdClass();
$this->request->keywords = "";
$this->request->search_results_per_page = 5;
$this->request->page_requested = 1;
$this->result = new StdClass();
$this->result->total_matches = null;
$this->result->result_set = array();
}
// ...
}
$search = new search();
var_dump($search->request, $search->result);
?>

There's several things to suggest here
First, if you're using PHP 5.3 or later, consider using namespaces and autoloading. That would allow you to make classes like \Cat\Search and \Dog\Search.
Second, I would avoid setting object properties directly. I would highly suggest you make your variables protected and use getters and setters instead. Limit your object interaction to methods and you can control the process much more easily.
$search_handler = new search_handler();
$search_handler->setKeywords(array("cats", "dogs"));

You could define your request and result classes as separately, and then have variables in your main class be assigned to instances of those.
Something like:
class result{
...
}
class request{
...
}
class search{
var results;
var request;
function __construct(){
$results = new ArrayObject();
$request = new stdClass();
}
...
}
Then you can assign/append to the list of results in your search function
and you can just assign a request object to your 'request' variable
$this->results[] = $newresult;
and loop through them when displaying

Related

Accessing class properties in PHP OOP

I am new in PHP OOP and was wondering if someone could help me with this.
I have a basic class with one method which returns data from database. Currently I am calling the method which displays everything inside the function.
Here is my class Definition:
class Products{
//properties
public $familyName = "";
public $familyProduct = "";
//Methods
public function getFamily($catId){
global $conn;
$sql = "SELECT * FROM product_family WHERE catID = '$catId'";
$result = $conn->query($sql);
if($result->num_rows > 0){
while($row = $result->fetch_assoc()){
echo "<li>".$row['familyName']."</li>";
echo "<li>".$row['familyProduct']."</li>";
}
}
}
}
Here is how I call the method:
$Products = new Products;
$Products->getFamily( 4 );
This works however, how can I assign each data coming from database ( ex familyName, familyProduct ) into variables inside class implementation and then access them individually where ever I need to. Something like this:
$Products = new Products;
$Products->familyName;
$Products->familyProduct;
I have empty properties but I am not sure how can I assign values to them coming from the loop and then return them each.
Thanks,
There are view things I would change in your Code.
Don't make Properties public use use Getters and Setters.
This will protect you Object from being used the wrong way e.g. now you can't change the familyName from outside: $products->familyName = "some value" because this would make the data of the object corrupt.
global $conn; is a no go in OOP use the construct of the Object,
in your case $products = new Products($conn);
Now you can set a Cat ID $products->setCatId(4); and read the result
$familyName = $products->getFamilyName(); or $familyProduct = $products->getFamilyProduct();
If you have more than one result you will get an array, if catId will always result one row you can delete this part. If you learn more about OOP you will find out that the hole SQL stuff can be done with a separate Object, but this is off Topic.
class Products
{
// Properties
protected $conn;
protected $catId;
protected $familyName;
protected $familyProduct;
public function __construct($conn)
{
$this->conn = $conn;
}
// set Cat ID and get date
public function setCatId($catId)
{
$this->catId = (int) $catId;
$this->getDate();
}
public function getCatId()
{
return $this->catId;
}
// get Family Name
public function getFamilyName()
{
return $this->familyName;
}
// get Family Product
public function getFamilyProduct()
{
return $this->familyProduct;
}
// get date
protected function getDate()
{
$sql = "SELECT * FROM product_family WHERE catID = '$this->catId'";
$result = $this->conn->query($sql);
// Default if no result
$this->familyName = null;
$this->familyProduct = null;
// if one Result
if ($result->num_rows == 1)
{
$row = $result->fetch_assoc();
$this->familyName = $row['familyName'];
$this->familyProduct = $row['familyProduct'];
}
if ($result->num_rows > 1)
{
$this->familyName = [];
$this->familyProduct = [];
while ($row = $result->fetch_assoc())
{
$this->familyName[] = $row['familyName'];
$this->familyProduct[] = $row['familyProduct'];
}
}
}
}

My First Class - Count Twitter Reactions

I'm working on a class that will count twitter reactions to a link and also display them.
Currently I'm working on the counting portion and my count always equals 0 even though the array created in the constructor has multiple elements.
Any help would be appreciated. Thanks.
<?php
class TwitterReactions{
function __construct($url){
if($url){
$output=array();
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results){
foreach($results as $result){
$output['user'][]=$result['from_user'];
$output['image'][]=$result['profile_image_url'];
$output['message'][]=$result['text'];
$output['date'][]=$result['created_at'];
}
}
return $output['user'];
} else {
echo "<p>Please provide a url...</p>";
}
}
function count_reactions($output){
//print_r($output);
$count = count($output['user']);
return $count;
}
}
?>
I agree with some of what Aliaksandr Astashenkau has in his answer but there are still some problems with the class.
It looks to me as if your initial problem was that you were expecting the __construct to return $output and you were then passing the object that you created into the count_reactions() method. Something like this...
$twitter = new TwitterReactions($url);
$count = $twitter->count_reactions($twitter);
You don't have your call to the count_reactions() method posted so this is just a hunch. If that's how you were using it the constructor it isn't meant to be used that way. Constructors always return a new instance of the class. You cannot return any other type of value from a constructor. You cannot use the return keyword in the __construct method.
As Aliaksandr Astashenkau points out $output should be a class member. I would also make $count a class member. In this case there's not much of a point of making either private so you don't really need accessor methods either but you can include them if you want.
I would make the class something like this...
<?php
class TwitterReactions
{
public $url = '';
public $output = array();
public $count = 0;
function __construct($url)
{
$this->url = $url;
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results)
{
foreach($results as $key => $result)
{
// I find it easier if the data is arranged by each tweet but you can keep the array structure how you have it.
$this->output[$key]['user'] = $result['from_user'];
$this->output[$key]['image'] = $result['profile_image_url'];
$this->output[$key]['message'] = $result['text'];
$this->output[$key]['date'] = $result['created_at'];
}
}
$this->count = count($this->output);
}
}
You could then use the class like this
$twitter = new TwitterReactions($url);
// you now have access to output directly
$twitter->output;
// and count
$twitter->count;
Anyhow there are many ways to accomplish the same thing but I hop this helps give you some ideas.
You probably want to make an $output array to be a property of your class. Then $this->output would be availbale in count_reactions method.
<?php
class TwitterReactions {
public $output;
function __construct($url){
if($url){
$output=array();
$query = 'http://search.twitter.com/search.json?q='.$url;
$reactions=file_get_contents($query);
$reactions_array=json_decode($reactions, TRUE);
foreach($reactions_array as $results){
foreach($results as $result){
$output['user'][]=$result['from_user'];
$output['image'][]=$result['profile_image_url'];
$output['message'][]=$result['text'];
$output['date'][]=$result['created_at'];
}
}
$this->output = $output;
return $output['user'];
} else {
echo "<p>Please provide a url...</p>";
}
}
function count_reactions($output){
//print_r($this->output);
$count = count($this->output['user']);
return $count;
}
}

Changing mysql result to "Class" with methods?

I have seen these codes:
$result = $db->result($query);
$rows = $result->fetchAll();
how can I do similar effect? ($result contains methods?)
I think this is what you are looking for:
<?php
class test{
private $value = 0;
function foo(){
$this->value = 1;
return $this;
}
function bar(){
$this->value = 2;
echo $this->value;
}
}
$test = new test();
$result = $test->foo();
$result->bar();
?>
By having the method return itself, you can chain them together in this fashion.
Strictly speaking, you're asking about OOP in PHP, in which case, this is a reasonable example:
class HasResultMethod
{
public function result( $query )
{
return new HasFetchAllMethod();
}
}
class HasFetchAllMethod
{
public function fetchAll(){}
}
// you have a variable with a result method that has one parameter.
$result = $db->result($query);
// that returns an object which has a fetchAll method.
$rows = $result->fetchAll();
You probably are dealing with some wrapper around PDO, a library to interface with databases. Their query methods will return a PDOStatement which has methods which will allow you to get results from the DB. result is either a typo, or it behaves in a very similar way.
I got it already. What a great hint Headspin
http://sandbox.phpcode.eu/g/147bd.php
<?php
class foo{
function bar(){
return $this;
}
function fetch(){
echo "yeah";
}
}
$foo = new foo();
$result = $foo->bar();
$result->fetch();
That is easy
$db is instance of class that returnes class, so when you say
$db->result($query);
$db will return object
e.g.
//this method is inside $db class
function result($query)
{
$result = new Result();
$result->rows = mysql_query...
return $result;
}
and when you say
$result->fetchAll();
that is method inside class Result that will fetch all rows saved inside $result->rows;
e.g.
//method inside Result class
function fetchAll()
{
//fetch rows inside variable $this->rows
}
So basically what you can do with ORM (object relational mapping), you can return Array of objects, each object will represent one record from db
e.g.
Class User
{
var $ID;
var $Name;
var $LastName;
var $Email;
function load($row)
{
$this->ID = $row["ID"];
... etc
}
function save()
{
$sql = "update tbl_users set Name=:Name, LastName=:LastName, Email=:Email where ID=:ID";
//then execute your query
}
}
so how to get list of objects, its easy
select all records and add them into array
$ar = new Array();
for($i = 0; $i < count($rows); $i++)
{
$r = new User();
$r->load($rows[$i]);
}
return $ar;
simple as that...

Object Oriented PHP Arrays

I've never tried OO PHP before so I decided to make a simple CMS to learn more. I am having a problem loading values into a multi-dimensional array.
class Article {
private $index = 0;
private $article;
public function Article() {
$get_articles = mysql_query("SELECT * FROM `articles`");
while ($result = mysql_fetch_array($get_articles)) {
echo $result["article"];
$this->article[$index]["Tags"] = $result["tags"];
$this->article[$index]["Categories"] = $result["categories"];
$this->article[$index]["Date"] = $result["date"];
$this->article[$index]["Article"] = $result["article"];
$this->article[$index]["URL"] = $result["url"];
$index++;
}
}
public function getArticle($articleID) {
return $this->article[$articleID]["Article"];
}
public function getTags($articleNumber) {
}
public function getCategories($articleNumber) {
}
public function getDate($articleNumber) {
}
}
The line echo $result["article"] outputs the one and only article value just fine, but apparently doesn't put it into the array?
$art = new Article();
echo $art->getArticle(0);
This doesn't output the article however. Would someone so kindly point out my noob mistake?
You didn't initialize your array.
$this->article = array();
while ($result = mysql_fetch_array($get_articles)) {
$this->article[$index] = array();
You probably should define your $index variable before using it in the loop. Maybe set it to the primary key field you retrieved from your query.
<?php
$index = $result['id'];
$this->article[$index]['tags'] = ...
You also need to initialize the $article member variable.
<?php
class Article {
private $article = array();
Remember that you define member variables within a class to be referenced via $this-> so you also don't need to define private $index = 0; in your class definition. Just define it inside the method.
You'll notice you used $this->article but not $this->index if you want to keep track of the length for the life of the object you'll need to replace $index with $this->index

Get PHP class property by string

How do I get a property in a PHP based on a string? I'll call it magic. So what is magic?
$obj->Name = 'something';
$get = $obj->Name;
would be like...
magic($obj, 'Name', 'something');
$get = magic($obj, 'Name');
Like this
<?php
$prop = 'Name';
echo $obj->$prop;
Or, if you have control over the class, implement the ArrayAccess interface and just do this
echo $obj['Name'];
If you want to access the property without creating an intermediate variable, use the {} notation:
$something = $object->{'something'};
That also allows you to build the property name in a loop for example:
for ($i = 0; $i < 5; $i++) {
$something = $object->{'something' . $i};
// ...
}
What you're asking about is called Variable Variables. All you need to do is store your string in a variable and access it like so:
$Class = 'MyCustomClass';
$Property = 'Name';
$List = array('Name');
$Object = new $Class();
// All of these will echo the same property
echo $Object->$Property; // Evaluates to $Object->Name
echo $Object->{$List[0]}; // Use if your variable is in an array
Something like this? Haven't tested it but should work fine.
function magic($obj, $var, $value = NULL)
{
if($value == NULL)
{
return $obj->$var;
}
else
{
$obj->$var = $value;
}
}
Just store the property name in a variable, and use the variable to access the property. Like this:
$name = 'Name';
$obj->$name = 'something';
$get = $obj->$name;
There might be answers to this question, but you may want to see these migrations to PHP 7
source: php.net
It is simple, $obj->{$obj->Name} the curly brackets will wrap the property much like a variable variable.
This was a top search. But did not resolve my question, which was using $this. In the case of my circumstance using the curly bracket also helped...
example with Code Igniter get instance
in an sourced library class called something with a parent class instance
$this->someClass='something';
$this->someID=34;
the library class needing to source from another class also with the parents instance
echo $this->CI->{$this->someClass}->{$this->someID};
Just as an addition:
This way you can access properties with names that would be otherwise unusable$x = new StdClass;
$prop = 'a b';
$x->$prop = 1;
$x->{'x y'} = 2;
var_dump($x);object(stdClass)#1 (2) {
["a b"]=>
int(1)
["x y"]=>
int(2)
}(not that you should, but in case you have to).
If you want to do even fancier stuff you should look into reflection
In case anyone else wants to find a deep property of unknown depth, I came up with the below without needing to loop through all known properties of all children.
For example, to find $foo->Bar->baz->bam, given an object ($foo) and a string like "Bar->baz->bam".
trait PropertyGetter {
public function getProperty($pathString, $delimiter = '->') {
//split the string into an array
$pathArray = explode($delimiter, $pathString);
//get the first and last of the array
$first = array_shift($pathArray);
$last = array_pop($pathArray);
//if the array is now empty, we can access simply without a loop
if(count($pathArray) == 0){
return $this->{$first}->{$last};
}
//we need to go deeper
//$tmp = $this->Foo
$tmp = $this->{$first};
foreach($pathArray as $deeper) {
//re-assign $tmp to be the next level of the object
// $tmp = $Foo->Bar --- then $tmp = $tmp->baz
$tmp = $tmp->{$deeper};
}
//now we are at the level we need to be and can access the property
return $tmp->{$last};
}
}
And then call with something like:
$foo = new SomeClass(); // this class imports PropertyGetter trait
echo $foo->getProperty("bar->baz->bam");
Here is my attempt. It has some common 'stupidity' checks built in, making sure you don't try to set or get a member which isn't available.
You could move those 'property_exists' checks to __set and __get respectively and call them directly within magic().
<?php
class Foo {
public $Name;
public function magic($member, $value = NULL) {
if ($value != NULL) {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
$this->$member = $value;
} else {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
return $this->$member;
}
}
};
$f = new Foo();
$f->magic("Name", "Something");
echo $f->magic("Name") , "\n";
// error
$f->magic("Fame", "Something");
echo $f->magic("Fame") , "\n";
?>
What this function does is it checks if the property exist on this class of any of his child's, and if so it gets the value otherwise it returns null.
So now the properties are optional and dynamic.
/**
* check if property is defined on this class or any of it's childes and return it
*
* #param $property
*
* #return bool
*/
private function getIfExist($property)
{
$value = null;
$propertiesArray = get_object_vars($this);
if(array_has($propertiesArray, $property)){
$value = $propertiesArray[$property];
}
return $value;
}
Usage:
const CONFIG_FILE_PATH_PROPERTY = 'configFilePath';
$configFilePath = $this->getIfExist(self::CONFIG_FILE_PATH_PROPERTY);
$classname = "myclass";
$obj = new $classname($params);
$variable_name = "my_member_variable";
$val = $obj->$variable_name; //do care about the level(private,public,protected)
$func_name = "myFunction";
$val = $obj->$func_name($parameters);
why edit:
before : using eval (evil)
after : no eval at all. being old in this language.

Categories