Ok, i have managed to end at this one.
Controller:
$addProperty=Property::addProperty($title,$description,$location,
$agent,$owner,$lat,$long,$position,$photoHolder,
$stars,$negatives,$adverts,$dropLink,$photosSite);
Model:
public static function addProperty($title,$description,$location,$agent,
$owner,$lat,$long,$position,
$photoHolder,$stars,$negatives,
$adverts,$dropLink,$photosSite)
The problem is that, not only, i have too many parameters but i need to pass around 10 more.
Any advice?
There's quite a few ways you could do this. My preferred way though when working with models is to have a set method for each attribute. That way you don't need to pass everything all at once (very useful as an application evolves and stuff gets added/removed).
So in a model, I would usually have something like this:
class Property {
private $title;
private $description;
private $location;
/**
* Creates an instance of property via a static method.
*
*/
public static factory()
{
return new Property();
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function setLocation($location)
{
$this->location = $location;
return $this;
}
// because the attributes in this instance are private I would also need getters
public function getTitle()
{
return $title;
}
public function getDescription()
{
return $description;
}
public function getLocation()
{
return $location;
}
}
Then you can also add in a save() method or whatever else you want it to do.
OK, so I've added a new static method called factory which allows you to create an instance without having to assign it to a variable. In addition to that I have added return $this; to all methods which do not return an attribute.
What this effectively means is you can now do this:
// create a new property
Property::factory()
->setTitle($title)
->setDescription($description)
->setLocation($location)
->save(); // if you had that function
The beauty of this is that if you did need to have a break in it, then the following would also work.
// create a new property
$property = Property::factory()
->setTitle($title)
->setDescription($description); // this is returning the property instance `return $this`
// do some processing to get the $location value
// continue creating the new property
$property
->setLocation($location)
->save(); // if you had that function
better way is to pass parameter as an array :
$params=array(
'title'=>'title',
'other_parameter'=>'value',
);
$addProperty=Property::addProperty($params);
Related
I have always done :
class Class1{
protected $myProperty;
public function __construct( $property ){
$this->myProperty = $property;
}
}
But recently, I have been coming across a particular technique like so:
class Class2{
protected $myProperty;
public function __construct( $property ){
$this->myProperty = $property;
return $this;
}
}
And in instantiating this class, one would do :
$property = 'some value';
$class1 = new Class1( $property );
$class2 = new Class2( $property );
What is the significance of the line return $this in the constructor of Class2 since with or without it, the variable $class2 will still contain an instance of Class2?
Edit : please this is different from a constructor returning values. I heard this one is called fluent interfaces (for method chaining). I have looked at this thread Constructor returning value?. It is not the same thing I am asking. I am asking for the significance of return $this
There isn't a use for returning $this there.
Chances are that they are using a IDE which automatically inserts return $this or similar, which is useful for method chaining, but the return statement to __construct is discarded.
return $this; should not have any value in the constructor. But I see some value if it is returned in any other function for the class, when you want to call the functions consecutively. For example :
class Student {
protected $name;
public function __construct($name) {
$this->name = $name;
//return $this; (NOT NEEDED)
}
public function readBook() {
echo "Reading...";
return $this;
}
public function writeNote() {
echo "Writing...";
return $this;
}
}
$student = new Student("Tareq"); //Here the constructor is called. But $student will get the object, whether the constructor returns it or not.
$student->readBook()->writeNote(); //The readBook function returns the object by 'return $this', so you can call writeNote function from it.
So I have a complex object which I wish to cache after creation as it is expensive to initialize. I'm able to reconstitute an instance within the class defining file but I need to be able to return the reconstituted instance in place of a new MyClass if my scheme is going to be of any use. (Don't I?)
Here's what I've done so far:
class PayPeriodService
{
public $type; // weekly,bi-weekly, semi-monthly, monthlly
public $payday_first;
public $close_first;
public $hours_start;
public $hours_end;
public $length; // in days
public $periods; // array of all periods this year
public $dayInterval;
public $oneWeekInterval;
public $twoWeekInterval;
public $semiFirstInterval;
public $monthInterval;
public $initialYear;
public $today; // date object
public function __construct()
{
if( Redis::exists('pay-period-instance')) {
Log:info( 'Fetching Pay-Period from cache.');
$instance = json_decode(Redis::get('pay-period-instance'));
// var_dump( $instance );
// exit();
return $instance;
}
return $this->init();
}
public function init()
{
Log::info('Reconstituting Pay-Period from primitive definition.');
$ppdef = PayPeriod::all()->last();
// etc etc etc, setting up all the properties, loading arrays etc
// finally I cache the object
Redis::set('pay-period-instance', json_encode($this));
return $this;
}
}
So when I instantiate this class, with $ppsvc = new PayPeriodService; in another class, the $instance variable in the PayPeriodService file is valid and fully reconsituted, fully functional. But the returned instance in $ppsvc is a mindless zombie shell of what it ought to be: no instance data, no methods.
What is the magic I need to invoke to get the restored object to travel abroad as it needs must do? I have explored the Serializable interface, and tried with un/serialize in place of the json_encode/decode with no significant change to my problem.
The problem is that __construct() method does NOT return anything. What you want is a singleton (AFAICU).
Look at this example:
class A {}
class B {
public function __construct(){return new A;}
}
$b = new B;
print_r($b); // B
So has you see even having the constructor returning a different class, that is not going to happen. There are several ways to accomplish this, so you can take a look on the web.
A simple example:
class PayPeriodService {
/**
* #var self
*/
static private $instance;
// private removes the possibility to make a new instance
private function __construct()
{
// object construction logic here
}
/**
* #return PayPeriodService
*/
static public function getInstance()
{
if(!self::$instance)
{
self::$instance = new static;
}
return self::$instance;
}
}
$ppsv = PayPeriodService::getInstance(); // will return what you intend
Unless the object is constantly mutating on Redis, this will do the trick. But you can easily adapt if needed
I'm designing a little CMS using PHP whilst putting OOP into practice. I've hit a problem though.
I have a page class, whos constructor accepts a UID and a slug. This is then used to set the properties (unless the page don't exist in which case it would fail). Anyway, I wanted to implement a function to create a page, and I thought ... what's the best way to do this without overloading the constructor. What would the correct way, or more conventional method, of doing this be?
My code is below:
<?php
class Page {
private $dbc;
private $title;
private $description;
private $image;
private $tags;
private $owner;
private $timestamp;
private $views;
public function __construct($uid, $slug) {
}
public function getTitle() {
return $this->title;
}
public function getDescription() {
if($this->description != NULL) {
return $this->description;
} else {
return false;
}
}
public function getImage() {
if($this->image != NULL) {
return $this->image;
} else {
return false;
}
}
public function getTags() {
if($this->tags != NULL) {
return $this->tags;
} else {
return false;
}
}
public function getOwner() {
return $this->owner;
}
public function getTimestamp() {
return $this->timestamp;
}
public function getViews() {
return $this->views;
}
public function createPage() {
// Stuck?
}
}
PHP Doesn't support overloading. I would probably go for something like:
$oPage = new Page();
$oPage->setTitle($sSomeTitle);
$oPage->setcontent($sSomeContent);
$oPage->save();
Or you could create an extra object - that is responsible for handling this.
$oPage = new Page();
$oPage->setTitle($sSomeTitle);
$oPage->setcontent($sSomeContent);
PageManager::create($oPage);
The advantage of this - is that your page class remains a pure data container, where all logic for loading/saving is done within an separate class.
I put those arguments in a constructor without which an object cannot exist. Other (optional) attributes of an object can be set by calling its setters.
For example, in your case a page seems to always have a UID, without which your system cannot recognize it, so it should be an argument to the constructor and I would build it such that the callee of the constructor shall not be able to call an empty constructor, because it won't mean anything for the page class.
A page can have no content and not even any title, but it has no meaning for the system without a UID or for example a URI. So my callee's code would look as follows:
$page = new Page($uid);
$page->setTitle($sSomeTitle);
$page->setcontent($sSomeContent);
$page->render();
However, my discussion mainly pertains to modelling your class. I personally think that the second solution in Wesley's answer is better.
You can make static method, that returns Page object
public static function CreatePageWithExtraAttribute($uid, $slug, $extra) {
$page = new Page($uid,$slug);
//your code here
return $page;
}
Or make a static method that querys database for example
public static function CreatePage($extra) {
list($uid,$slug) = SomeDatabaseManager::GetUidSlug();
$page = new Page($uid,$slug);
//your code here
return $page;
}
Then use it
$page=Page::CreatePageWithExtraAttribute($title)
I have a ORM-type class that I use to update rows in the database. When I pass an object of this class to my DAO, I want the DAO to only update the fields in the object that changed (the SQL query should only contain the changed columns). Right now I'm just keeping track of every time a setter method is called and using this to determine which fields changed.
But this means I have to duplicate the same code in every setter method. Is there a way in PHP that I can create a method which is automatically called any time any method in the class is called? The __call magic method only works for non-existent methods. I want something like that, but for existing methods.
Here's the code that I have so far:
class Car{
private $id;
private $make;
private $model;
private $modifiedFields = array();
public function getMake(){ return $this->make; }
public function setMake($make){
$this->make = $make;
$this->modified(__METHOD__);
}
//getters and setters for other fields
private function modified($method){
if (preg_match("/.*?::set(.*)/", $method, $matches)){
$field = $matches[1];
$field[0] = strtolower($field[0]);
$this->modifiedFields[] = $field;
}
}
}
This is what I want:
class Car{
private $id;
private $make;
private $model;
private $modifiedFields = array();
public function getMake(){ return $this->make; }
public function setMake($make){
//the "calledBeforeEveryMethodCall" method is called before entering this method
$this->make = $make;
}
//getters and setters for other fields
private function calledBeforeEveryMethodCall($method){
if (preg_match("/.*?::set(.*)/", $method, $matches)){
$field = $matches[1];
$field[0] = strtolower($field[0]);
$this->modifiedFields[] = $field;
}
}
}
Thanks.
You could name all your setters in a generic way, like:
protected function _setABC
and define __call as something like:
<?php
public function __call($name, $args) {
if (method_exists($this, '_', $name)) {
return call_user_func_array(array($this, '_', $name), $args);
}
}
as there are no enums in PHP I tried to do something like this:
class CacheMode{
public static $NO_CACHE = new CacheMode(1, "No cache");
private $id, $title;
public function getId(){
return $this->id;
}
public function getTitle(){
return $this->title;
}
private function __construct($id, $title){
$this->id = $id;
$this->title = $title;
}
}
The problem is, that I get a parse error if I run the script:
Parse error: syntax error, unexpected T_NEW
I "worked it aroud" with this:
class CacheMode{
public static function NO_CACHE(){
return new CacheMode(1, __("No cache",'footballStandings'));
}
public static function FILE_CACHE(){
return new CacheMode(2, __("Filecache",'footballStandings'));
}
public static function VALUES(){
return array(self::NO_CACHE(), self::FILE_CACHE());
}
private $id, $title;
public function getId(){
return $this->id;
}
public function getTitle(){
return $this->title;
}
private function __construct($id, $title){
$this->id = $id;
$this->title = $title;
}
}
It works, but I am not really happy with it.
Can anyone explain, why I can't do the static $xyz = new XYZ(); way or has a better solution for this problem?
Its annoying, I know. I solve it like
class Foo {
public static $var;
}
Foo::$var = new BarClass;
Its a little bit similar to javas "static code blocks" (or whatever they are called ^^)
The file is only includeable once anyway (because a "class already define" error occurs), so you can be sure, that also the code below the class is executed once.
As an optimization, you could store the object instance as a static field, so that you are not creating a new object every time the static method is called:
private static $noCache;
public static function NO_CACHE(){
if (self::$noCache == null){
self::$noCache = new CacheMode(1, __("No cache",'footballStandings'));
}
return self::$noCache;
}
But yes, it is annoying that you cannot assign a new object instance to a class field when you first define the field. :(
Quoting the manual page of static :
Like any other PHP static variable,
static properties may only be
initialized using a literal or
constant; expressions are not allowed.
So while you may initialize a static
property to an integer or array (for
instance), you may not initialize it
to another variable, to a function
return value, or to an object.
That is why you cannot do
public static $NO_CACHE = new CacheMode(1, "No cache");