I have a Zend_Form object that I want to re-use several times in one page. The problem I'm having is that each time it's rendered it has the same element IDs. I've been unable to find a method for giving all the IDs a unique prefix or suffix each time I render the form.
Complete Solution
Subclass Zend_Form:
class My_Form extends Zend_Form
{
protected $_idSuffix = null;
/**
* Set form and element ID suffix
*
* #param string $suffix
* #return My_Form
*/
public function setIdSuffix($suffix)
{
$this->_idSuffix = $suffix;
return $this;
}
/**
* Render form
*
* #param Zend_View_Interface $view
* #return string
*/
public function render(Zend_View_Interface $view = null)
{
if (!is_null($this->_idSuffix)) {
// form
$formId = $this->getId();
if (0 < strlen($formId)) {
$this->setAttrib('id', $formId . '_' . $this->_idSuffix);
}
// elements
$elements = $this->getElements();
foreach ($elements as $element) {
$element->setAttrib('id', $element->getId() . '_' . $this->_idSuffix);
}
}
return parent::render($view);
}
}
Loop in the view script:
<?php foreach ($this->rows as $row) : ?>
<?php echo $this->form->setDefaults($row->toArray())->setIdSuffix($row->id); ?>
<?php endforeach; ?>
You may subclass Zend_Form and overload render method to generate id's automatically:
public function render()
{
$elements = $this->getElements();
foreach ($elements as $element) {
$element->setAttrib('id', $this->getName() . '_' . $element->getId();
}
}
This is just a pseudo-code. Of course, you may modify this to suit your needs.
You could add a static integer property (let's say self::$counter)to your Zend_Form inherited class.
You increment it on the init() method.
For each element you create on your Zend_Form object you append that property to your element :
$element->setAttrib('id', self::$counter + '_myId');
Related
So, I've created a webshop system. It all worked perfectly, except for that projects can always be improved. So someone told me that 'Templating' and 'Routing' would be nice improvements. I've written classes for both of them, and they work fine! Now, I do wish to know how to combine them? How can I combine these classes so that data from the routing (to determine the content) needs to be placed inside the template. How would I do this?
Templating class:
class Template
{
private $assignedValues = array();
private $tpl;
/*
** #description Creates one single instance of itself and checks whether the template file exists.
** #param $path [string] This is the path to the template
*/
public function __construct($_path = '')
{
if(!empty($_path)){
if(file_exists($_path)){
$this->tpl = file_get_contents($_path);
}
else{
echo '<b>Template Error:</b> File Inclusion Error.';
}
}
}
/*
** #description Assign a value to a part in the template.
** #param $_searchString [string] This is the part in the template that needs to be replaced
** #param $_replaceString [string] This is the code/text that will replace the part in the template
*/
public function assign($_searchString, $_replaceString)
{
if(!empty($_searchString)){
$this->assignedValues[strtoupper($_searchString)] = $_replaceString;
}
}
/*
** #description Shows the final result of the page.
*/
public function show()
{
if(count($this->assignedValues > 0)){
foreach ($this->assignedValues as $key => $value) {
$this->tpl = str_replace('{'.$key.'}', $value, $this->tpl);
}
}
echo $this->tpl;
}
/*
** #description Quickly load a part of the page
** #param $quickLoad [string] This is the name of the file that will be loaded and assigned
** #param $_searchString [string] This is the part in the template that needs to be replaced
*/
public function quickLoad($_searchString, $part)
{
if(file_exists(INCLUDES.DS.$part.'.php')){
$this->assign($_searchString,include(INCLUDES.DS.$part.'.php'));
}
else{
return "That file does not exist!";
}
}
}
And the routing class:
class Route
{
protected $controller = 'App';
protected $method = 'Call';
protected $params = [];
/*
** #description Loads the classes and methods which are referred to.
*/
public function __construct()
{
$url = $this->parseUrl();
if($this->checkUrl())
{
unset($url[0]);
if(isset($url[1]))
{
if (file_exists('core/classes/' . $url[1] . '.class.php'))
{
$this->controller = $url[1];
unset($url[1]);
}
}
require_once('core/classes/' . $this->controller . '.class.php');
$this->controller = new $this->controller;
if (isset($url[2]))
{
if (method_exists($this->controller, $url[2]))
{
$this->method = $url[2];
unset($url[2]);
}
}
$this->params = $url ? array_values($url) : [];
$this->arrayUrl($this->params);
call_user_func_array([$this->controller, $this->method], $this->params);
}
}
/*
** #description Check whether the URL part contains a string
*/
public function checkUrl($index = '0',$value = 'Route'){
$url = $this->parseUrl();
if($url[$index] == $value){
return true;
}
return false;
}
/*
** #description Splits the url into pieces.
*/
protected function parseUrl()
{
if(isset($_GET['url']))
{
return $url = explode('/', filter_var(rtrim(urldecode($_GET['url']), '/'), FILTER_SANITIZE_URL));
}
}
/*
** #description Sets arrays in routes.
*/
protected function arrayUrl($params = array())
{
foreach($params as $index => $param)
{
if (preg_match('/>/',$param))
{
$newParam = explode('>', $param);
unset($this->params[$index]);
$this->params['fields'][$newParam[0]] = $newParam[1];
}
else{
unset($this->params[$index]);
$this->params[] = $param;
}
}
print_r($this->params);
}
}
I can access my routes by URL's like this:
http://localhost:8080/Webshop/Route/User/logout
With: Class & Method.
This is a simple example that I already use, because no data needs to be showed using this method. It only logs out the user that is logged in. After that, you get redirected to the home page. But how could I use routing for other pages? For example, update some user data without having to create an update file?
EDIT:
This is an example of a page I use now (index.php):
<?php
/*
** #description Includes config.php once so that we can use classes, defines etcetera.
*/
require_once('core/preferences/config.php');
/*
** #description Instanciate new route object.
*/
$route = new Route();
/*
** #description Check if a route isset. When not, continue, else: run route
*/
if(!$route->checkUrl())
{
/*
** #description Instanciate new template object.
*/
$template = new Template(TEMPLATES_PATH .'/camerashop24.tpl.html');
/*
** #description Assign values.
*/
$template->assign('title', 'Home');
$template->assign('root', '');
$template->quickLoad('loginout', 'loginout');
$template->quickLoad('title_block', 'title_block');
$template->quickLoad('cart','cart');
$template->quickLoad('menu', 'menu');
$template->assign('page', 'Home');
$breadcrumbs = new Breadcrumbs($_SERVER["REQUEST_URI"],'');
$template->assign('breadcrumbs', $breadcrumbs->data());
$content = "";
foreach(explode(",",Config::get('settings/front_page_cat')) as $item) {
$content .= "<div id='blue-box' class='blue-box'><h2 style='color: white;'>" . strtoupper($item) . "</h2></div>";
$category = new Category();
$category->find($item);
if($category->exists($item)){
foreach (explode(",",$category->data()->products) as $item) {
$product = new Product($item);
$product->find($item);
$content .= '' . $product->showProduct($product->data()->type,$product->data()->name,$product->data()->price) . '';
}
}
}
$template->assign('text', $content);
$template->quickLoad('footer','footer');
/*
** #description Showing content.
*/
$template->show();
}
But, what I want as an answer, how can I show the data from the routing (select users profile for example) in this template without having to create a page for it like this one.
I think you're gonna have to modify your Routing class. Ideally you want the routing class to give YOU the ability to tell it what controller the route should use. Th controller you select would act as the middle man for processing. Each controller's method would represent a route, something like this:
/**
* URL = www.test.com/index.php/home
* URL = www.test.com/index.php/about
*
* ROUTE Class:
* - should define routes. Example:
*
* $route["/home"] = MainController/home
* $route["/about"] = MainController/about
* $route["/*"] = MainController/*
*
*/
class MainController
{
public function home()
{
$template = new Template(TEMPLATES_PATH . 'home.tpl.html');
$template->show();
}
public function about()
{
$template = new Template(TEMPLATES_PATH . 'about.tpl.html');
$template->show();
}
}
This is probably a stupid question but I can't figure out what to google. I'm learning OOP and get the general concept but trying to implement it and I'm getting some questions.
I'm making a web scraper using simple html dom. I want to create a class which connects to a page and contains several different functions I can call on that page depending on what I want to do. I then can call this classes methods from another file.
This is the class I have written:
<?php
include_once '/simple_html_dom.php';
class CountyScraping
{
protected $demoMode = 0; //set 1 for demo
protected $cinemas = NULL;
protected $url = NULL;
protected $html = NULL;
protected $counties = array();
function __construct($url)
{
$this->setUrl($url);
}
public function setUrl($url)
{
$this->url = $url;
}
public function getUrl()
{
return $this->url;
}
public function setHtml()
{
$this->html = file_get_html($this->url);
}
public function getHtml()
{
return $this->html;
}
public function setCounties()
{
foreach($this->html->find('#UserLocation option') as $element)
{
if (($element->value != NULL) && ($element->plaintext))
{
$county = array();
$county['id'] = $element->value;
$county['county_name'] = $element->plaintext;
$this->counties[] = $county;
}
}
}
public function getCounties()
{
return $this->counties;
}
?>
As you can see from the class above I'm looking to get a list of counties from my page and it's all working as should be. The main method I want to call is to get an array of counties. Using the code above to get a list of counties I have to do something like this currently:
$scrape = new CountyScraping("http://example.com");
$scrape->setHtml();
$scrape->setCounties();
$counties = $scrape->getCounties();
This works fine and I could continue with this however I feel like I'm making a lot of calls to getters and setters from outside the class. I think I should be making one call, getCounties and have everything be handled inside the class.
Am I correct in assuming this? Should my getCounties() method call my setHtml and setCounties methods? Or should I be keeping my getters and setters as minimal as possible and have another function instead?
Any advice about any part of my code would be welcome.
PHP magic methods will help you a bit more also.
Here is a link to the php manual for __set()
http://php.net/manual/en/language.oop5.overloading.php#object.set
here is also a nice articale that will assist you with more information on magic methods
http://culttt.com/2014/04/16/php-magic-methods/
Here is a basic example of magic methods
<?php
class PropertyTest
{
/** Location for overloaded data. */
private $data = array();
/** Overloading not used on declared properties. */
public $declared = 1;
/** Overloading only used on this when accessed outside the class. */
private $hidden = 2;
public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** As of PHP 5.1.0 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** As of PHP 5.1.0 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** Not a magic method, just here for example. */
public function getHidden()
{
return $this->hidden;
}
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>
Output is here:
Setting 'a' to '1'
Getting 'a'
1
Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)
1
Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'
Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29
This example has been sourced from http://php.net/manual/en/language.oop5.overloading.php#object.set
I would suggest you to always use an SOA (service oriented architecture) when you want to develop with OOP. With SOA your service coming from the class CountyScraper should be unique. This is why we use a singleton. A service shouldn't keep a state from an action. This is why you shouldn't set the url or html as properties.
<?php
include_once '/simple_html_dom.php';
class CountyScraper
{
/**
* Returns the *Singleton* instance of this class.
*
* #staticvar Singleton $instance The *Singleton* instances of this class.
*
* #return Singleton The *Singleton* instance.
*/
public static function getInstance()
{
static $instance = null;
if (null === $instance) {
$instance = new static();
}
return $instance;
}
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct()
{
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* #return void
*/
private function __clone()
{
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* #return void
*/
private function __wakeup()
{
}
public function findCounties($url)
{
$counties = array();
$html = file_get_html($url);
foreach ($html->find('#UserLocation option') as $element)
{
if (($element->value != NULL) && ($element->plaintext))
{
$county = array();
$county['id'] = $element->value;
$county['county_name'] = $element->plaintext;
$counties[] = $county;
}
}
}
}
Then, you can use it like that:
$countyScraper = CountyScraper::getInstance();
$countyScraper->findCounties('http://example.com');
I've recently learned about the advantages of using Dependency Injection (DI) in my PHP application.
However, I'm still unsure how to create my container for the dependencies. Before, I use a container from a framework and I want to understand how is he doing things in back and reproduce it.
For example:
The container from Zend 2. I understand that the container make class dynamic, he does not have to know about them from the beginning, he checks if he already has that class in his registry and if he has not he check if that class exist and what parameters has inside constructor and put it in his own registry so next time could take it from there, practical is doing everything dynamic and it is completing his own registry, so we do not have to take care of nothing once we implement the container as he can give as any class we want even if we just make that class.
Also if I want to getInstance for A which needs B and B needs C I understand that he doing this recursive and he goes and instantiate C then B and finally A.
So I understand the big picture and what is he suppose to do but I am not so sure about how to implement it.
You may be better off using one of the existing Dependency Containers out there, such as PHP-DI or Pimple. However, if you are looking for a simpler solution, then I've implemented a Dependency Container as part of an article that I wrote here: http://software-architecture-php.blogspot.com/
Here is the code for the container
class Container implements \DecoupledApp\Interfaces\Container\ContainerInterface
{
/**
* This function resolves the constructor arguments and creates an object
* #param string $dataType
* #return mixed An object
*/
private function createObject($dataType)
{
if(!class_exists($dataType)) {
throw new \Exception("$dataType class does not exist");
}
$reflectionClass = new \ReflectionClass($dataType);
$constructor = $reflectionClass->getConstructor();
$args = null;
$obj = null;
if($constructor !== null)
{
$block = new \phpDocumentor\Reflection\DocBlock($constructor);
$tags = $block->getTagsByName("param");
if(count($tags) > 0)
{
$args = array();
}
foreach($tags as $tag)
{
//resolve constructor parameters
$args[] = $this->resolve($tag->getType());
}
}
if($args !== null)
{
$obj = $reflectionClass->newInstanceArgs($args);
}
else
{
$obj = $reflectionClass->newInstanceArgs();
}
return $obj;
}
/**
* Resolves the properities that have a type that is registered with the Container.
* #param mixed $obj
*/
private function resolveProperties(&$obj)
{
$reflectionClass = new \ReflectionClass(get_class($obj));
$props = $reflectionClass->getProperties();
foreach($props as $prop)
{
$block = new \phpDocumentor\Reflection\DocBlock($prop);
//This assumes that there is only one "var" tag.
//If there are more than one, then only the first one will be considered.
$tags = $block->getTagsByName("var");
if(isset($tags[0]))
{
$value = $this->resolve($tags[0]->getType());
if($value !== null)
{
if($prop->isPublic()) {
$prop->setValue($obj, $value);
} else {
$setter = "set".ucfirst($prop->name);
if($reflectionClass->hasMethod($setter)) {
$rmeth = $reflectionClass->getMethod($setter);
if($rmeth->isPublic()){
$rmeth->invoke($obj, $value);
}
}
}
}
}
}
}
/**
*
* #param string $dataType
* #return object|NULL If the $dataType is registered, the this function creates the corresponding object and returns it;
* otherwise, this function returns null
*/
public function resolve($dataType)
{
$dataType = trim($dataType, "\\");
$obj = null;
if(isset($this->singletonRegistry[$dataType]))
{
//TODO: check if the class exists
$className = $this->singletonRegistry[$dataType];
$obj = $className::getInstance();
}
else if(isset($this->closureRegistry[$dataType]))
{
$obj = $this->closureRegistry[$dataType]();
}
else if(isset($this->typeRegistry[$dataType]))
{
$obj = $this->createObject($this->typeRegistry[$dataType]);
}
if($obj !== null)
{
//Now we need to resolve the object properties
$this->resolveProperties($obj);
}
return $obj;
}
/**
* #see \DecoupledApp\Interfaces\Container\ContainerInterface::make()
*/
public function make($dataType)
{
$obj = $this->createObject($dataType);
$this->resolveProperties($obj);
return $obj;
}
/**
*
* #param Array $singletonRegistry
* #param Array $typeRegistry
* #param Array $closureRegistry
*/
public function __construct($singletonRegistry, $typeRegistry, $closureRegistry)
{
$this->singletonRegistry = $singletonRegistry;
$this->typeRegistry = $typeRegistry;
$this->closureRegistry = $closureRegistry;
}
/**
* An array that stores the mappings of an interface to a concrete singleton class.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $singletonRegistry;
/**
* An array that stores the mappings of an interface to a concrete class.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $typeRegistry;
/**
* An array that stores the mappings of an interface to a closure that is used to create and return the concrete object.
* The key/value pair corresond to the interface name/class name pair.
* The interface and class names are all fully qualified (i.e., include the namespaces).
* #var Array
*/
private $closureRegistry;
}
The above code can be found here: https://github.com/abdulla16/decoupled-app (under the /Container folder)
You can register your dependencies as a singleton, as a type (every time a new object will be instantiated), or as a closure (the container will call the function that you register and that function is expected to return the instance).
For example,
$singletonRegistry = array();
$singletonRegistry["DecoupledApp\\Interfaces\\UnitOfWork\\UnitOfWorkInterface"] =
"\\DecoupledApp\\UnitOfWork\\UnitOfWork";
$typeRegistry = array();
$typeRegistry["DecoupledApp\\Interfaces\\DataModel\\Entities\\UserInterface"] =
"\\DecoupledApp\\DataModel\\Entities\\User";
$closureRegistry = array();
$closureRegistry["DecoupledApp\\Interfaces\\DataModel\\Repositories\\UserRepositoryInterface"] =
function() {
global $entityManager;
return $entityManager->getRepository("\\DecoupledApp\\DataModel\\Entities\\User");
};
$container = new \DecoupledApp\Container\Container($singletonRegistry, $typeRegistry, $closureRegistry);
This Container resolves properties of a class as well as the constructor parameters.
I have done a very simple IoC class which works as intended. I've investigated the IoC and DI pattern and especially after reading this answer. Let me know if something is not right or you have any questions .
<?php
class Dependency {
protected $object = null;
protected $blueprint = null;
/**
* #param $instance callable The callable passed to the IoC object.
*/
public function __construct($instance) {
if (!is_object($instance)) {
throw new InvalidArgumentException("Received argument should be object.");
}
$this->blueprint = $instance;
}
/**
* (Magic function)
*
* This function serves as man-in-the-middle for method calls,
* the if statement there serves for lazy loading the objects
* (They get created whenever you call the first method and
* all later calls use the same instance).
*
* This could allow laziest possible object definitions, like
* adding annotation parsing functionality which can extract everything during
* the call to the method. once the object is created it can get the annotations
* for the method, automatically resolve its dependencies and satisfy them,
* if possible or throw an error.
*
* all arguments passed to the method get passed to the method
* of the actual code dependency.
*
* #param $name string The method name to invoke
* #param $args array The array of arguments which will be passed
* to the call of the method
*
* #return mixed the result of the called method.
*/
public function __call($name, $args = array())
{
if (is_null($this->object)) {
$this->object = call_user_func($this->blueprint);
}
return call_user_func_array(array($this->object, $name), $args);
}
}
/*
* If the object implements \ArrayAccess you could
* have easier access to the dependencies.
*
*/
class IoC {
protected $immutable = array(); // Holds aliases for write-protected definitions
protected $container = array(); // Holds all the definitions
/**
* #param $alias string Alias to access the definition
* #param $callback callable The calback which constructs the dependency
* #param $immutable boolean Can the definition be overriden?
*/
public function register ($alias, $callback, $immutable = false) {
if (in_array($alias, $this->immutable)) {
return false;
}
if ($immutable) {
$this->immutable[] = $alias;
}
$this->container[$alias] = new Dependency($callback);
return $this;
}
public function get ($alias) {
if (!array_key_exists($alias, $this->container)) {
return null;
}
return $this->container[$alias];
}
}
class FooBar {
public function say()
{
return 'I say: ';
}
public function hello()
{
return 'Hello';
}
public function world()
{
return ', World!';
}
}
class Baz {
protected $argument;
public function __construct($argument)
{
$this->argument = $argument;
}
public function working()
{
return $this->argument->say() . 'Yep!';
}
}
/**
* Define dependencies
*/
$dic = new IoC;
$dic->register('greeter', function () {
return new FooBar();
});
$dic->register('status', function () use ($dic) {
return new Baz($dic->get('greeter'));
});
/**
* Real Usage
*/
$greeter = $dic->get('greeter');
print $greeter->say() . ' ' . $greeter->hello() . ' ' . $greeter->world() . PHP_EOL . '<br />';
$status = $dic->get('status');
print $status->working();
?>
I think the code is pretty self-explanatory, but let me know if something is not clear
Because I haven't find anything near what I wanted,I tried to implement on my own a container and I want to hear some opinion about how is looking,because I've start to learn php and oop a month ago a feedback is very important for me because I know I have many things to learn,so please feel free to bully my code :))
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<?php
class ioc
{
private $defs;
static $instance;
private $reflection;
private function __construct()
{
$defs = array();
$reflection = array();
}
private function __clone()
{
;
}
public static function getInstance()
{
if (!self::$instance) {
self::$instance = new ioc();
}
return self::$instance;
}
public function getInstanceOf($class)
{
if (is_array($this->defs) && key_exists($class, $this->defs)) {
if (is_object($this->defs[$class])) {
return $this->defs[$class];
}
} else {
if (class_exists($class)) {
if (is_array($this->reflection) && key_exists($class, $this->reflection)) {
$reflection = $this->reflection[$class];
} else {
$reflection = new ReflectionClass($class);
$this->reflection[$class] = $reflection;
}
$constructor = $reflection->getConstructor();
if ($constructor) {
$params = $constructor->getParameters();
if ($params) {
foreach ($params as $param) {
$obj[] = $this->getInstanceOf($param->getName());
}
$class_instance = $reflection->newInstanceArgs($obj);
$this->register($class, $class_instance);
return $class_instance;
}
}
if (!$constructor || !$params) {
$class_instance = new $class;
$this->register($class, $class_instance);
return $class_instance;
}
}
}
}
public function register($key, $class)
{
$this->defs[$key] = $class;
}
}
?>
i use
$myblogrepo = $this->_doctrine->getRepository('Entities\Blog')->findBy(array('id' => 12);
i access via
foreach($myblogrepo as $key =>$value){
echo $key . $value;
}
how can i get the field names? i thought the key => would work but it print s the key as 0
so i thought this would work:
foreach($myblogrepo[0] as $key =>$value){
echo $key . $value;
}
but still nothing..
}
In all likelihood, your Blog entity's properties are declared as protected. This is why you can't iterate over them from outside the the Entity itself.
If you're using your Blog entities in a read-only fashion, and only need access to the properties marked as #Columns (read: you don't need to call any methods on your entity), you might consider using array-hydration. That way you'll be dealing with simple arrays, and $k=>$v type iteration will work fine.
Otherwise, you'll need to create some kind of getValues() method on your entity class. This could be a simple implementation that just builds and array and returns it.
Finally, you could create a general-purpose getValues() as a utility function that uses doctrine's class metadata to figure out what columns and entity has, and operate on those data. A simple implementation like this:
function getEntityColumnValues($entity,$em){
$cols = $em->getClassMetadata(get_class($entity))->getColumnNames();
$values = array();
foreach($cols as $col){
$getter = 'get'.ucfirst($col);
$values[$col] = $entity->$getter();
}
return $values;
}
EDIT - A more mature version of the above method seems to be available here - I've not played with it yet, but it looks promising.
If you just need to get the properties of the entity in a fast and easy way, this is what I do in my projects:
All my entities inherit from an EntityBase class, which has the following method:
public function toValueObject()
{
$result = new \stdClass();
foreach ($this as $property => $value) {
$getter = 'get' . ucfirst($property);
if (method_exists($this, $getter)) {
$result->$property = $this->$getter();
}
}
return $result;
}
So all I have to do is call $entity->toValueObject() and I obtain a standard object with all of the entity's properties as public properties.
Use findOneBy instead of findBy to select a single row.
$myblogrepo = $this->_doctrine->getRepository('Entities\Blog')->findOneBy(array('id' => 12);
Your key was 0 because it was the first row in a possible multi-row result.
This is a my implementation of a serializer class that also check if it is a doctrine entity:
/**
* JsonApiSerializer constructor.
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* #param $payLoad
* #return string
*/
public function serialize($payLoad, $type)
{
$serializedPayload = new \stdClass();
$serializedPayload->data = new \stdClass();
$serializedPayload->data->type = $type;
if ($this->isDoctrineEntity($payLoad)) {
$this->addEntityColumnValues($serializedPayload, $payLoad);
}
return json_encode($serializedPayload);
}
private function isDoctrineEntity($payLoad)
{
if (is_object($payLoad)) {
$payLoad = ($payLoad instanceof Proxy)
? get_parent_class($payLoad)
: get_class($payLoad);
}
return !$this->em->getMetadataFactory()->isTransient($payLoad);
}
private function addEntityColumnValues(&$serializedPayload, $entity){
$serializedPayload->data->attributes = new \stdClass();
$classMetaData = $this->em->getClassMetadata(get_class($entity));
$columnNames = $classMetaData->getColumnNames();
foreach($columnNames as $columnName){
$fieldName = $classMetaData->getFieldForColumn($columnName);
$getter = 'get'.ucfirst($fieldName);
$serializedPayload->data->attributes->$columnName = $entity->$getter();
}
}
Ive some classes that share some attributes, and i would like to do something like:
$dog = (Dog) $cat;
is it possible or is there any generic work around?
Its not a superclass, or a interface or related in any way. They are just 2 different classes i would like php map the attributes from a cat class to a dog and give me the new object. –
i guess i have to specify a little bit more cause seem like a senseless thing to do.
i've classes that inherits from different parent classes cause i've made an inheritance tree based on the saving method, maybe my bad from the beginning, but the problem is that i have a lot of classes that are practically equal but interacts one with mysql and the other one with xml files. so i have:
class MySql_SomeEntity extends SomeMysqlInteract{}
and
Xml_SomeEntity extends SomeXmlInteract{}
its a little bit deeper tree but the problem its that. i cant make them inherits from the same class cause multiple inheritance is not allowed, and i cant separate current interaction with superclases cause would be a big trouble.
Basically the attributes in each one are practical the same.
since i have a lot of this matching classes i would like to do some generic casting or something like it that can converts (pass the values to each attribute) and but im trying to search the simplest way to everyone of this classes.
You can use above function for casting not similar class objects (PHP >= 5.3)
/**
* Class casting
*
* #param string|object $destination
* #param object $sourceObject
* #return object
*/
function cast($destination, $sourceObject)
{
if (is_string($destination)) {
$destination = new $destination();
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationReflection = new ReflectionObject($destination);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
if ($destinationReflection->hasProperty($name)) {
$propDest = $destinationReflection->getProperty($name);
$propDest->setAccessible(true);
$propDest->setValue($destination,$value);
} else {
$destination->$name = $value;
}
}
return $destination;
}
EXAMPLE:
class A
{
private $_x;
}
class B
{
public $_x;
}
$a = new A();
$b = new B();
$x = cast('A',$b);
$x = cast('B',$a);
There is no built-in method for type casting of user defined objects in PHP. That said, here are several possible solutions:
1) Use a function like the one below to deserialize the object, alter the string so that the properties you need are included in the new object once it's deserialized.
function cast($obj, $to_class) {
if(class_exists($to_class)) {
$obj_in = serialize($obj);
$obj_out = 'O:' . strlen($to_class) . ':"' . $to_class . '":' . substr($obj_in, $obj_in[2] + 7);
return unserialize($obj_out);
}
else
return false;
}
2) Alternatively, you could copy the object's properties using reflection / manually iterating through them all or using get_object_vars().
This article should enlighten you on the "dark corners of PHP" and implementing typecasting on the user level.
Without using inheritance (as mentioned by author), it seems like you are looking for a solution that can transform one class to another with preassumption of the developer knows and understand the similarity of 2 classes.
There's no existing solution for transforming between objects. What you can try out are:
get_object_vars() : convert object to array
Cast to Object: convert array to object
You do not need casting. Everything is dynamic.
I have a class Discount.
I have several classes that extends this class:
ProductDiscount
StoreDiscount
ShippingDiscount
...
Somewhere in the code I have:
$pd = new ProductDiscount();
$pd->setDiscount(5, ProductDiscount::PRODUCT_DISCOUNT_PERCENT);
$pd->setProductId(1);
$this->discounts[] = $pd;
.....
$sd = new StoreDiscount();
$sd->setDiscount(5, StoreDiscount::STORE_DISCOUNT_PERCENT);
$sd->setStoreId(1);
$this->discounts[] = $sd;
And somewhere I have:
foreach ($this->discounts as $discount){
if ($discount->getDiscountType()==Discount::DISCOUNT_TYPE_PRODUCT){
$productDiscount = $discount; // you do not need casting.
$amount = $productDiscount->getDiscountAmount($this->getItemTotalPrice());
...
}
}// foreach
Where getDiscountAmount is ProductDiscount specific function, and getDiscountType is Discount specific function.
a better aproach:
class Animal
{
private $_name = null;
public function __construct($name = null)
{
$this->_name = $name;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
if ($to instanceof Animal) {
$to->_name = $this->_name;
} else {
throw(new Exception('cant cast ' . get_class($this) . ' to ' . get_class($to)));
return $to;
}
public function getName()
{
return $this->_name;
}
}
class Cat extends Animal
{
private $_preferedKindOfFish = null;
public function __construct($name = null, $preferedKindOfFish = null)
{
parent::__construct($name);
$this->_preferedKindOfFish = $preferedKindOfFish;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
parent::cast($to);
if ($to instanceof Cat) {
$to->_preferedKindOfFish = $this->_preferedKindOfFish;
}
return $to;
}
public function getPreferedKindOfFish()
{
return $this->_preferedKindOfFish;
}
}
class Dog extends Animal
{
private $_preferedKindOfCat = null;
public function __construct($name = null, $preferedKindOfCat = null)
{
parent::__construct($name);
$this->_preferedKindOfCat = $preferedKindOfCat;
}
/**
* casts object
* #param Animal $to
* #return Animal
*/
public function cast($to)
{
parent::cast($to);
if ($to instanceof Dog) {
$to->_preferedKindOfCat = $this->_preferedKindOfCat;
}
return $to;
}
public function getPreferedKindOfCat()
{
return $this->_preferedKindOfCat;
}
}
$dogs = array(
new Dog('snoopy', 'vegetarian'),
new Dog('coyote', 'any'),
);
foreach ($dogs as $dog) {
$cat = $dog->cast(new Cat());
echo get_class($cat) . ' - ' . $cat->getName() . "\n";
}
It sounds like what you really want to do is implement an interface.
Your interface will specify the methods that the object can handle and when you pass an object that implements the interface to a method that wants an object that supports the interface, you just type the argument with the name of the interface.
You may think about factories
class XyFactory {
public function createXyObject ($other) {
$new = new XyObject($other->someValue);
// Do other things, that let $new look like $other (except the used class)
return $new;
}
}
Otherwise user250120s solution is the only one, which comes close to class casting.
class It {
public $a = '';
public function __construct($a) {
$this->a = $a;
}
public function printIt() {
;
}
}
//contains static function to 'convert' instance of parent It to sub-class instance of Thing
class Thing extends it {
public $b = '';
public function __construct($a, $b) {
$this->a = $a;
$this->b = $b;
}
public function printThing() {
echo $this->a . $this->b;
}
//static function housed by target class since trying to create an instance of Thing
static function thingFromIt(It $it, $b) {
return new Thing($it->a, $b);
}
}
//create an instance of It
$it = new It('1');
//create an instance of Thing
$thing = Thing::thingFromIt($it, '2');
echo 'Class for $it: ' . get_class($it);
echo 'Class for $thing: ' . get_class($thing);
Returns:
Class for $it: It
Class for $thing: Thing
I think that the best approach is to just create a new instance of a class and than assign the object. Here's what I would do:
public function ($someVO) {
$someCastVO = new SomeVO();
$someCastVO = $someVO;
$someCastVO->SomePropertyInVO = "123";
}
Doing this will give you code hinting in most IDEs and help ensure you are using the correct properties.
If the object you are trying to cast from or to has properties that are also user-defined classes, and you don't want to go through reflection, you can use this.
<?php
declare(strict_types=1);
namespace Your\Namespace\Here
{
use Zend\Logger; // or your logging mechanism of choice
final class OopFunctions
{
/**
* #param object $from
* #param object $to
* #param Logger $logger
*
* #return object
*/
static function Cast($from, $to, $logger)
{
$logger->debug($from);
$fromSerialized = serialize($from);
$fromName = get_class($from);
$toName = get_class($to);
$toSerialized = str_replace($fromName, $toName, $fromSerialized);
$toSerialized = preg_replace("/O:\d*:\"([^\"]*)/", "O:" . strlen($toName) . ":\"$1", $toSerialized);
$toSerialized = preg_replace_callback(
"/s:\d*:\"[^\"]*\"/",
function ($matches)
{
$arr = explode(":", $matches[0]);
$arr[1] = mb_strlen($arr[2]) - 2;
return implode(":", $arr);
},
$toSerialized
);
$to = unserialize($toSerialized);
$logger->debug($to);
return $to;
}
}
}
You can opt for this example below. Hope it will help.
/** #var ClassName $object */
$object->whateverMethod() // any method defined in the class can be accessed by $object
I know this is not a cast but it can be useful sometimes.
PHP provides a very simple way of doing this by using:
(object) ['id'=>1,'name'=>'cat']
https://www.php.net/manual/en/language.types.object.php
In your case you try this:
$dog = json_encode($dog);
$cat = (object) json_decode($dog)
More optimize method is:
$dog = (array)$dog;
$dog['newfield'] = 'xyz';
$dog = (object)$dog;