When I do the following:
$arUserStuff = array ('name' => 'username', 'email' => 'test#test.com');
$object = (object) $arUserStuff;
print_r($object);
The print function returns me the following:
stdClass Object ( [name] => username [email] => test#test.com )
How can I change std class object in let's say's User Object?
Create that class, then create an object of it:
class User {
public $name, $email; // public for this example, or set these by constructor
public function __construct( array $fields) {
foreach( $fields as $field => $value)
$this->$field = $value;
}
}
$object = new User;
$object->name = 'username';
$object->email = 'test#test.com';
Or, you can do:
$arUserStuff = array ('name' => 'username', 'email' => 'test#test.com');
$object = new User( $arUserStuff);
Now, from print_r( $object);, you'll get something like this:
User Object ( [name] => username [email] => test#test.com )
actually to do what you want, you should make it like:
$arUserStuff = new ArrayObject(
array (
'name' => 'username', 'email' => 'test#test.com'
)
);
to change the class name you need to create a new class.
It's a rather complex process but you can learn about it here:
http://php.net/manual/en/language.oop5.php
Here's a generic function that converts an array into any type of object, assuming the fields are public
class User { public $name, $email; }
class Dog { public $name, $breed; }
function objFromArray($className, $arr) {
$obj = new $className;
foreach(array_keys(get_class_vars($className)) as $key) {
if (array_key_exists($key, $arr)) {
$obj->$key = $arr[$key];
}
}
return $obj;
}
print_r(objFromArray('User',
array ('name' => 'username', 'email' => 'test#test.com')));
echo "<br/>";
print_r(objFromArray('Dog',
array ('name' => 'Bailey', 'breed' => 'Poodle')));
Output
User Object ( [name] => username [email] => test#test.com )
Dog Object ( [name] => Bailey [breed] => Poodle )
I wanted to make a trait out of it but don't have PHP 5.4 installed to test it. This wouldn't require the fields to be public
trait ConvertibleFromArray {
public static function fromArray($arr) {
var $cls = get_called_class();
var $obj = new $cls;
foreach($arr as $key=>$value) {
if (property_exists($obj, $arr)) {
$obj->$key = $value;
}
}
return $obj;
}
}
class User {
use ConvertibleFromArray;
public $name, $email;
}
class Dog {
use ConvertibleFromArray;
public $name, $breed;
}
print_r(User::fromArray(array ('name' => 'username', 'email' => 'test#test.com')));
print_r(Dog::fromArray(array('name' => 'Bailey', 'breed' => 'Poodle')));
?>
Related
I have a simple ZF2 application that uses two tables and a service and I'm trying to convert it to run on ZF3. I can't work out how to update the service manager code. Here's an example of one of the controllers
<?php
namespace LibraryRest\Controller;
use Zend\Mvc\Controller;
use Library\Service\SpydusServiceInterface;
use Library\Service\SpydusService;
use Library\Model\BookTitle;
use Library\Model\BookTitleTable;
use Library\Model\Author;
use Library\Model\AuthorTable;
use Zend\Mvc\Controller\AbstractRestfulController;
use Zend\View\Model\JsonModel;
class SearchRestController extends AbstractRestfulController {
protected $bookTitleTable;
public function getBookTitleTable() {
if (! $this->bookTitleTable) {
$this->bookTitleTable = $this->getServiceLocator ()->get ( 'BookTitleTable' );
}
return $this->bookTitleTable;
}
protected $authorTable;
public function getAuthorTable() {
if (! $this->authorTable) {
$sm = $this->getServiceLocator ();
$this->authorTable = $sm->get ( 'AuthorTable' );
}
return $this->authorTable;
}
protected $spydusService;
public function __construct(SpydusServiceInterface $spydusService) {
$this->spydusService = $spydusService;
}
public function getList($action, $first, $last) {
if ($action == 'search') {
return $this->searchAction ( $first, $last );
}
}
public function searchAction() {
$message = array ();
$results = array ();
$first = urldecode ( $this->params ()->fromRoute ( 'first' ) );
$last = urldecode ( $this->params ()->fromRoute ( 'last' ) );
if ($this->getAuthorTable ()->getAuthorId ( $first, $last ) === false) {
$results [0] = array ('count' => 0, 'message' => 'This author not found in database', 'first' => $first, 'last' => $last, 'title' => '', 'titleCode' => '', 'link' => '', 'authorId' => '' );
} else {
$authorId = $this->getAuthorTable ()->getAuthorId ( $first, $last )->author_id;
$books = $this->spydusService->searchBooks ( $first, $last );
$count = count ( $books );
foreach ( $books as $foundTitle ) {
if ($foundTitle->getMessage () == 'Nothing found') {
$results [0] = array ('count' => 0, 'message' => 'Nothing found by library search', 'first' => $first, 'last' => $last, 'title' => '', 'titleCode' => '', 'link' => '', 'authorId' => '' );
break;
}
$searchBooks = $this->getBookTitleTable ()->getId ( $foundTitle->getTitle (), $authorId );
if ($searchBooks->count () == 0) {
$addUrl = "http://newlib.rvw.dyndns.org/library/search/add/" . $authorId . '/' . $foundTitle->getTitle ();
$results [] = array ('count' => $count, 'message' => $foundTitle->getMessage (), 'title' => $foundTitle->getTitle (), 'titleCoded' => $foundTitle->getTitleCoded (), 'first' => $foundTitle->getAuthorFirst (), 'last' => $foundTitle->getAuthorLast (), 'link' => $foundTitle->getLink (), 'authorId' => $authorId, 'addUrl' => $addUrl );
}
}
}
if (count ( $results ) == 0) {
$results [0] = array ('count' => 0, 'message' => 'Nothing found by library search', 'first' => $first, 'last' => $last, 'title' => '', 'titleCode' => '', 'link' => '', 'authorId' => '' );
}
return new JsonModel ( $results );
}
}
What code should I use instead of the getServiceLocator() call as this is no longer supported in ZF3? Elsewhere on Stack Overflow someone had replied to another question and suggested using a createService function but this has been dropped from ZF3 as well.
There are a couple of different approaches, but you're already using the most common one: passing the dependencies in through the constructor. You're currently doing this for your $spydusService class, so change the constructor to also accept arguments for the two table clases, something like:
class SearchRestController extends AbstractRestfulController
{
protected $bookTitleTable;
protected $authorTable;
protected $spydusService;
public function __construct(SpydusServiceInterface $spydusService, $bookTitleTable, $authorTable)
{
$this->spydusService = $spydusService;
$this->bookTitleTable = $bookTitleTable;
$this->authorTable = $authorTable;
}
[etc.]
}
then, somewhere you already have a factory for the SearchRestController (it might be a closure in your Module.php class, or a standalone factory class). You'll want to modify this to pass in the extra arguments:
public function getControllerConfig()
{
return array(
'factories' => array(
'SearchRestController' => function ($sm) {
$spydusService = $sm->get('SpydusService'); // this part you already have
$bookTitleTable = $sm->get('BookTitleTable');
$authorTable = $sm->get('AuthorTable');
return new SearchRestController($spydusService, $bookTitleTable, $authorTable);
}
)
);
}
You are going to want to create a factory to build controller. This new class will implement FactoryInterface. In the __invoke function, you'll use the $container instance to retrieve all of your dependencies of your controller and either pass them as arguments in the constructor or set them on the constructor instance. Then just return your controller instance from that function.
Your controller will need to be updated with fields to support this. You will also need to be sure to register your factory in your configuration.
I have a system where I am creating multiple classes that all extend from an abstract class.
Each class also declares 'settings' for that particular class type.
Example:
class First extends Base {
protected $name = 'First';
protected $lug = 'first';
protected $fields = [
'name',
'address',
'phone',
];
function __construct()
{
parent::__construct();
}
public function abstractMethod()
{
// do stuff for this particular class
}
}
and
class Second extends Base {
protected $name = 'Second';
protected $lug = 'second-one';
protected $fields = [
'first-name',
'last-name',
'email',
];
function __construct()
{
parent::__construct();
}
public function abstractMethod()
{
// do stuff for this particular class
}
}
Now what I want to be able to do is grab all extended classes and their 'settings' and return something like this:
$classes = [
'first' => [
'name' => 'First',
'slug' => 'first',
'fields' => ['name', 'address', 'phone']
],
'second' => [
'name' => 'Second',
'slug' => 'second-one',
'fields' => ['first-name', 'last-name', 'email']
]
];
So how would I go about doing this? Is there a better way?
I am using Laravel if that helps.
Edit: To Explain why not a duplicate
I'm not just after a way to get classes and their information I am after a way to architect this situation. I am essentially creating an extensible plugin system and need a way to Tell-Don't-Ask which plugins have been added.
I didn't try it, but it should work. Or it'll directs you.
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Base'))
$result[] = get_class_vars($class);
}
But your properties needs to be public also.
What about using ReflectionClass? Getting properties is quite easy, example from manual below. Listing extended classes should be easy too.
<?php
class Bar {
protected $inheritedProperty = 'inheritedDefault';
}
class Foo extends Bar {
public $property = 'propertyDefault';
private $privateProperty = 'privatePropertyDefault';
public static $staticProperty = 'staticProperty';
public $defaultlessProperty;
}
$reflectionClass = new ReflectionClass('Foo');
var_dump($reflectionClass->getDefaultProperties());
Output:
array(5) {
["staticProperty"]=>
string(14) "staticProperty"
["property"]=>
string(15) "propertyDefault"
["privateProperty"]=>
string(22) "privatePropertyDefault"
["defaultlessProperty"]=>
NULL
["inheritedProperty"]=>
string(16) "inheritedDefault"
}
Using ReflectionObject you can do it like this:
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'Base')) {
$obj = new $class;
$refObj = new ReflectionObject($obj);
$props = $refObj->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
$classProps = array();
foreach ($props as $prop) {
$property = $refObj->getProperty($prop->getName());
$property->setAccessible(true);
$classProps[$prop->getName()] = $property->getValue($obj);
}
$result[$class] = $classProps;
}
}
print_r($result);
Output:
Array (
[First] => Array (
[name] => First
[lug] => first
[fields] => Array (
[0] => name
[1] => address
[2] => phone
)
)
[Second] => Array (
[name] => Second
[lug] => second-one
[fields] => Array (
[0] => first-name
[1] => last-name
[2] => email
)
)
)
For a metering project, I have a class MeterReadings, with a constructor that takes a previous reading as basis for all of its property values except the timestamp. In the example, the Object has only 3 properties, in real life there are many more. I could line by line for each property X do
$this->propertyX=$prevMS->propertyX
but that quickly becomes tedious.
What is best practice for copying all property values from the $prevMS object?
<?php class MeterReadings{
function __construct($prevMS=NULL){
if($prevMS===NULL){
$this->gas=0;
$this->water=0;
$this->electricity=0;
}else{
//PHP can duplicate an object by assignment,
//so I expected to be able to create a copy of $prevMS
//but cannot $this=$prevMS;
//and cannot $this= clone $that;
//which both throw error 'Cannot re-assign $this'
foreach($prevMS as $key => $value){
//logs "Undefined property: MeterReadings::$0"
print "$key => $value\n";
$this->$key = $prevMS->$value;
}
}
$this->date=time();//set timestamp for this object
}
}
$test= new MeterReadings();
$test2 = new MeterReadings($test);
print_r($test);
print_r($test2);
?>gets me
gas => 0
water => 0
electricity => 0
date => 1434448706
MeterReadings Object
(
[gas] => 0
[water] => 0
[electricity] => 0
[date] => 1434448706
)
MeterReadings Object
(
[gas] =>
[water] =>
[electricity] =>
[date] => 1434448706
)
You can use clone and the __clone Magic Method to manage what get's cloned.
For example:
class Test
{
public $value1;
public $value2;
public $timestamp;
public function __clone()
{
$this->timestamp = null;
}
}
$test = new Test();
$test->value1 = 1;
$test->value2 = 2;
$test->timestamp = time();
$test2 = clone $test;
print_r($test2);
// Test Object
// (
// [value1] => 1
// [value2] => 2
// [timestamp] =>
// )
Alternative:
To keep things as in the __constuct as specified the comment below:
class Test
{
public $value1;
public $value2;
public $timestamp;
public function __construct (Test $copy = null)
{
if($copy) {
foreach ($copy as $key => $value) {
$this->$key = $value;
}
}
$this->timestamp = 'whoop';
}
}
$test = new Test();
$test->value1 = 1;
$test->value2 = 2;
$test->timestamp = 'whatev\'s';
$test2 = new Test($test);
print_r($test2);
// Test Object
// (
// [value1] => 1
// [value2] => 2
// [timestamp] => 'whoop'
// )
This will work for objects that are not itterable and for properties that are protected (but not private). For objects that are itterable and return something else, you can use get_object_vars(), documentation here. For stuff that's private (are you sure you need private, that's unusual) you'll need to create getters. They can be protected if you don't want them externally available.
I am having trouble getting associative names of my $_errors array right.
$item1 = 'password';
$item2 = 'firstname';
$this->addError(array($item1 => 'required'))
$this->addError(array($item2 => 'required'))
private function addError($error) {
$this->_errors[] = $error;
}
public function error($item) {
return array_search($item, $this->_errors);
}
When I do a print_r() on $_errors I get:
Array
(
[0] => Array
(
[password] => required
)
[1] => Array
(
[firstname] => required
)
)
But I need it to be:
Array
(
[password] => required
[firstname] => required
)
So I can call 'password' like so $this->_errors['password'];
Simply change your functions accordingly:
private function addError($element, $error) {
$this->_errors[$element] = $error;
}
$this->addError($item1, 'required');
$this->addError($item2, 'required');
Of course this scheme will not allow you to track multiple errors for the same element at the same time; if you need to do that you have to rethink your desired result.
Use a list() construct to break the array into key-value pair and then subject it to your $this->errors[] array variable.
private function addError($error) {
$this->_errors[] = $error;
}
to
private function addError($error) {
list($a,$b) = each($error); //<--------- Add the list() here
$this->_errors[$a] = $b; //<--------- Map those variables to your array
}
I need to create a nested structure class of a multidimensional array, this is my array:
Array
(
[days] => Array
(
[0] => Array
(
[rows] => Array
(
[0] => Array
(
[activity_id] => 1
[name] => Activity 2
[city] => London
[info] => fsdsdshgsfd
)
[1] => Array
(
[activity_id] => 3
[name] => Activity 1
[city] => London
[info] => fsdhgsfd
)
)
)
[1] => Array
(
[rows] => Array
(
[0] => Array
(
[activity_id] => 3
[name] => Activity 1
[city] => London
[info] => fsdhgsfd
)
...
)
)
)
...
)
)
I have been trying to rewrite my code to make it class-driven, but I am struggling with that, what is the right way to build a class structure Itinerary->Days->Rows to replace an array? I tried something like this, I am not sure if it makes sense, I don't really understand the way how it has to be done:
class Itinerary
{
private $days = array();
public static function addDay($day) {
$this->$days[] = new ItineraryDay($day);
}
}
class ItineraryDay implements Countable
{
private $rows = array();
public static function addRow($row) {
$this->$rows[] = new ItineraryRow($row);
}
public function count()
{
return count($this->rows);
}
}
class ItineraryRow implements Countable
{
private $name;
private $city;
...
function __get($key)
{
...
}
function __set($key, $value)
{
...
}
public function count()
{
return count($this->rows);
}
}
$itinerary1 = new Itinerary();
$day1 = new ItineraryDay();
$itinerary1->addDay($day1);
$row1 = new ItineraryRow();
$day1->addRow($row1);
Can someone guide me?
It really depends what you ultimately want to do with said structure, but for a general idea I typically do something like this:
class Itinerary implements Countable
{
private $days;
public function __construct( array $days = array() )
{
$this->setDays( $days );
}
public function addDay( ItineraryDay $day )
{
$this->days[] = $day;
}
public function setDays( array $days )
{
$this->days = array();
foreach( $days as $day )
{
$this->addDay( $day );
}
}
public function count()
{
return count( $this->days );
}
}
class ItineraryDay implements Countable
{
private $rows;
public function __construct( array $rows = array() )
{
$this->setRows( $rows );
}
public function addRow( ItineraryRow $row )
{
$this->rows[] = $row;
}
public function setRows( array $rows )
{
$this->rows = array();
foreach( $rows as $row )
{
$this->addRow( $row );
}
}
public function count()
{
return count( $this->rows );
}
}
class ItineraryRow
{
private $id;
private $name;
private $city;
private $info;
public function __construct( $id, $name, $city, $info )
{
$this->id = $id;
$this->name = $name;
$this->city = $city;
$this->info = $info;
}
/* ... */
}
Then using it with the structure of your current array of data:
$days = array();
foreach( $originalData[ 'days' ] as $days )
{
$rows = array();
foreach( $days[ 'rows' ] as $row )
{
$rows[] = new ItineraryRow( $row[ 'activity_id' ], $row[ 'name' ], $row[ 'city' ], $row[ 'info' ] );
}
$days[] = new ItineraryDay( $rows );
}
$itinerary = new Itinerary( $days );
Or:
$itinerary = new Itinerary;
foreach( $originalData[ 'days' ] as $days )
{
$day = new ItineraryDay;
foreach( $days[ 'rows' ] as $row )
{
$row = new ItineraryRow( $row[ 'activity_id' ], $row[ 'name' ], $row[ 'city' ], $row[ 'info' ] )
$day->addRow( $row );
}
$itinerary->addDay( $day );
}
So, you can either pass "child" objects to the constructor (the method that constructs a new object), or add them with methods after construction. If you want the objects to be immutable, meaning you don't want to allow the objects to accept any more rows / days after construction, just make the addDay, setDays, addRow and setRows methods protected or private thereby only allowing passing "child" object through the constructors.
Be aware that, as PeeHaa already mentioned, you don't want static methods, because they operate class wide, not on individual instances of classes (objects). As a matter of fact, you cannot even use static methods the way you intended, because $this is only available in object context, not in class wide context.
But, to be honest, the question is a little bit to vague to be answered properly. We'd have to have a little more details about how you are going to construct the objects, and how you are going to use them later on.