I have a requirement where a process can be implemented in 2 different situation. One situation would be where the start date cannot be in the past and the other would be where it can.
Currently we utilise Value Objects where we perform a series of a validation items on each field submitted using Zend Validate objects.
The validation extends a base class e.g.
class ValueObject_test1 extends filter()
Filter is made up of: -
class filter {
protected $_filter;
protected $_filterRules = array();
protected $_validatorRules = array();
protected $_data = array();
protected $_objData = array();
protected $_options = array(Zend_Filter_Input::ESCAPE_FILTER => 'StripTags');
protected $_runValidation = true;
protected function _setFilter()
protected function _addFilter()
protected function _addValidator()
protected function _addData()
protected function _addObject()
protected function _addOption()
public function getData()
public function hasErrors()
public function getMessages()
public function getValidationState()
public function __get()
public function __isset()
public function __set()
}
ValueObject_test1 is made up of:
class ValueObject_test1 extends filter {
public function __construct($testVar) {
$this->_setData(testVar);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar) {
$this->_addData('testVar', $testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
What I'm trying to achieve is an extension of ValueObject_test1 so that my second situation will have an additional validation item as well as the items in ValueObject_test1()
I've written the following for my second situation:-
<?php
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar', $testVar);
$this->_addData('testVar2', $testVar2);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
The issue i'm having is that the output from this only appears to validate my second situation validation and nothing on the second. I'm under the impression that by setting both variables in _setData() the validation should occur for items in ValueObject_test1 and the items in my ValueObject_test2?
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
parent::__construct($testVar);
}
// _setFilters is identical to the parent implementation
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar2', $testVar2);
parent::_setData($testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
parent::_setValidators();
}
}
My code doesn't call the _setData correctly because the $this->setData(testVar) inside the parent::_construct's will call the $this->_setData(testVar) version of the function.
If you want to override a function, your override will likely want to also run the logic of the parent.
<?php
class Foo {
protected function _setFoobar() {
// Foo logic
}
}
class Bar extends Foo {
protected function _setFoobar() {
// custom Bar logic specific to the child class (Bar)
parent::_setFoobar();
}
}
Related
Say I have the following code, is there a way to somehow extend an abstract class on a child and require a different type of argument in the "overloaded" function. I want to insert various types of objects in the Collection through the add function. In some cases, I'd like to insert an Error object, sometimes some other (XYZ) object, and let's say that all those objects extend the same abstract class called Parent.
I would appreciate if somebody could tell me if something like this is even possible, and if it is suggest some ways to accomplish this. Note that production server on which I intend to host the application runs on php 5.6.40.
Thank you in advance.
namespace App;
use App\Models\Parent;
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
abstract public function add($key, Parent $item);
}
public class ErrorList extends Collection
{
public function __construct()
{
parent::__construct();
}
public function add($key, Error $item)
{
$this->collection[$key] = $item;
}
}
namespace App\Models;
abstract class Parent {}
public class Error extends Parent {}
public class XYZ extends Parent{}
Try this
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
//no type hinting
abstract public function add($key, $item);
}
class ErrorList extends Collection
{
// this constructor doing nothing , it can be removed and
// parent constructor will still be called unlike java or any other
// OOP
public function __construct()
{
parent::__construct();
}
//no type hinting
public function add($key, $item)
{
//code
}
}
If you're extending a class or implementing an interface the signature must match. You can however implement type checking yourself and type hint in a docblock.
As a side note, public class is invalid syntax.
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
abstract public function add($key, Parent $item);
}
class ErrorList extends Collection
{
public function __construct()
{
parent::__construct();
}
/**
* #param $key
* #param Parent|Error $item
*/
public function add($key, Parent $item)
{
if (!($item instanceof Error)) {
throw new \InvalidArgumentException('Unable to add object to error list: ' . get_class($item));
}
$this->collection[$key] = $item;
}
}
I have a base class which sets up's other extending controllers like this:
class BaseController extends Controller
{
public $globalCurrencies;
public $globalLanguages;
public function __construct()
{
$this->globalCurrencies = $this->getCurrencies(); // this works
$this->globalLanguages = $this->getLanguages(); // this works
}
}
And I use one of helpers to extend this class like this:
class SessionHelper extends BaseController
{
public $test;
public function __construct()
{
parent::__construct(); // fire parent aka basecontroller construct
$this->test = $this->globalCurrencies; // this works (variables are set)
echo '__construct: '.$this->test; // this even displays it
}
public function getCurrencies()
{
dd('method'.$this->test); // NOT WORKING
}
public function getCurrentCurrency()
{
return $this->getCurrencies()->where('id', Session::get('currencyId'))->first() ?? null;
}
}
Later on code is used in model:
class Product extends Model
{
protected $table = "products";
public $timestamps = true;
public $sessionHelper;
public function __construct()
{
$this->sessionHelper = new SessionHelper;
}
public function getPrice($conversion_rate = null)
{
return number_format($this->price_retail / $this->sessionHelper->getCurrentCurrency()->conversion_rate, 2);
}
}
Have any body idea why I can access in construct variable but not in method? If i remember correctly construct is fired first so everything after should have access to it.
Declare $test variable as private out side the constructor. Inside the constructor keep it the way you are doing it right now and then make a setter and getter for the test variable.
class testObject
{
private $test;
function __construct($test)
{
$this->test= $this->globalCurrencies;
}
// test getter
function getTest()
{
return $this->test;
}
}
Change your method to be;
public function getCurrencies()
{
dd('method', $this->test);
}
You can not concatenate strings and objects/arrays.
If that doesn't resolve the issue - check the laravel.log
I'm following along with some learning material on PHP and am now into abstract classes and methods as well as interfaces. While implementing interfaces I've encountered an error on first run-through. It happens while the classes and interfaces are being defined. I apologize in advance for the size of the code sample but I'd like to be circumspect.
The error I receive is:
'Fatal error: Declaration of DinnerMenu::setDinnerPortion() must be compatible with DinnerPortion::setDinnerPortion() in ... Menu.php on line 85'.
Menu.php follows:
<?php
abstract class Menu { // can't be instanciated, only extended from
private $_menuid,
$_menuitemid,
$_menuname,
$_description;
public function setMenuID($menuid) {$this->_menuid = $menuid;}
public function getMenuID() {return $this->_menuid;}
public function setMenuItemID($menuitemid) {$this->_menuitemid = $menuitemid;}
public function getMenuItemID() {return $this->_menuitemid;}
public function setMenuName($menuname) {$this->__menuname = $menuname;}
public function getMenuName() {return $this->_menuname;}
public function setDescription($description) {$this->_description = $description;}
public function getDescription() {return $this->_description;}
}
class MenuItem {
private $_menuitemid,
$_itemname,
$_description,
$_price,
$_servingsize,
$_picture;
public function setID($menuitemid) {$this->_menuitemid = $menuitemid;}
public function getID() {return $this->_menuitemid;}
public function setItemName($itemname) {$this->_itemname = $itemname;}
public function getItemName() {return $this->_itemname;}
public function setDescription($description) {$this->_description = $description;}
public function getDescription() {return $this->_description;}
public function setPrice($price) {$this->_price = $price;}
public function getPrice() {return $this->_price;}
public function setServingSize($servingsize) {$this->_servingsize = $servingsize;}
public function getServingSize() {return $this->_servingsize;}
public function setPicture($picture) {$this->_picture = $picture;}
public function getPicture() {return $this->_picture;}
}
class MainMenu extends Menu {
}
class DrinkMenu extends Menu {
}
class LunchMenu extends Menu {
}
final class KidsMenu extends Menu { // final keyword stops inheritance: cannot have sub-classes or child classes, cannot be overridden
}
final class DessertMenu extends Menu {
}
interface DinnerPortion {
public function setDinnerPortion();
}
interface DinnerPrices {
public function setDinnerPrices();
}
interface HappyHourDrinkPrices {
public function setHappyHourDrinkPrices();
}
final class DinnerMenu extends LunchMenu implements DinnerPortion, DinnerPrices {
public function setDinnerPortion($menuitemObject) {
$adjusted_servingsize = 1;
$base_servingsize = $menuitemObject->getServingSize();
// dinner portion 50% bigger than lunch portion
$adjusted_servingsize = $base_servingsize * 1.5;
return $adjusted_servingsize;
}
public function setDinnerPrices($menuitemObject) {
$adjusted_price = 1;
$base_price = $menuitemObject->getPrice();
// dinner price 25% more than lunch price
$adjusted_price = $base_price * 1.25;
return $adjusted_price;
}
}
final class HappyHourMenu extends DrinkMenu implements HappyHourDrinkPrices {
public function setHappyHourDrinkPRices($drinkObject) {
$adjusted_price = 1;
$base_price = $drinkObject->getPrice();
// happy hour drink prices 30% less than regular prices
$adjusted_price = $base_price * 0.7;
return $adjusted_price;
}
}
?>
setDinnerPortion in the interface doesn't have an argument, while the same method in the class DinnerMenu (which implements the interface) has the argument $menuitemObject.
An interface describes rules that a class must obey. In this case it defines that the class needs to implement this method without arguments.
If you want to use interfaces with their methods, you have to take over the methods which are described in the interface.
From your interface ;
public function setDinnerPortion($menuitemObject)
The above method requires an argument while the method in your class which implements the interface doesn't have it
public function setDinnerPortion()
Solution : add an argument.
in your class the signature is :
public function setDinnerPortion($menuitemObject)
although in your interface the function signature is :
public function setDinnerPortion();
These function signatures must be the same !
I'm working on a PHP Class, one of the required fields for the Class is (DropoffType)
The answer to that is REGULARPICKUP.
Can I write this inside the class and put "=" to the REGULARPICKUP?
Example:
protected $DropoffType = 'REGULARPICKUP';
Sure you can
class MyClass
{
protected $DropoffType = 'REGULARPICKUP';
}
or do it in the constructor (this depends on your specific case whether this is the way to go):
class MyClass
{
protected $DropoffType;
public function __construct($Dropofftype = 'REGULARPICKUP')
{
$this->DropoffType = $Dropofftype;
}
}
Or if you have a set function:
class MyClass
{
protected $DropoffType;
public function setDropoffType($Dropofftype = 'REGULARPICKUP')
{
$this->DropoffType = $Dropofftype;
}
}
I am writing some tests for a Magento module, using Ivan Chepurnyi's extension, and I'm having trouble using the mock objects.
Here is the class:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
public function __construct()
{
$this->_salesCollection = Mage::getModel('module/classA')->getCollection()
->addFieldToFilter('id', $this->_getId());
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_salesCollection->count();
}
}
The method I'm trying to test is getSalesTotalNumber().
And here is the test:
<?php
class Namespace_Module_Test_Block_Class extends EcomDev_PHPUnit_Test_Case
{
private $_mock;
public function setUp()
{
$this->_mock = $this->getMock('Namespace_Module_Block_Class',
array('_getId')
);
$this->_mock->expects($this->any())
->method('_getId')
->will($this->returnValue(1024));
parent::setUp();
}
/**
* #test
* #loadFixture
* #loadExpectation
*/
public function testSalesTotalNumber()
{
$actual = $this->_mock->getSalesTotalValue();
$expected = $this->_getExpectations()->getSalesTotalNumber();
$this->assertEquals($expected, $actual);
}
}
As you can see, what I want to do is overwrite the _getId() method so that it returns an id which match the id in the fixture and so load the collection. But it doesn't work :-(.
In my test, if I echo $this->_mock->_getId() it returns the correct Id (1024). But in the __construct() of my class $this->_getId() returns null, which is the expected value during testing (I mean, during testing there is no session, so it can't get the object's Id as I store it in a session variable). So the _getId() method isn't mocked by my test case.
Any help will be highly appreciated.
So my problem was not in the mock/test but in the class.
I have moved the content of __construct() into a protected method which returns the collection object. That's how my class looks like now:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
protected function _getAffiliateSales()
{
if (is_null($this->_salesCollection)) {
$affiliateId = $this->_getId();
$this->_salesCollection = Mage::getModel('module/classA')
->addFieldToFilter('id', $affiliateId);
}
return $this->_salesCollection;
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_getAffiliateSales()->count();
}
}