I'm really bad at OOP and I can't work out this inherited code I've been given.
This is part of the generic Model class;
abstract class Model
{
protected static $_tableName = false;
protected static $_itemName = false;
public static function tableName()
{
return static::$_tableName;
}
public static function itemName()
{
return static::$_itemName;
}
How do I set the tablename in the Class that I have created???;
class Payments extends Model {
//public $_tableName;
public function __construct()
{
$this->$_tableName = 'payments'; //line 13
}
}
I get an error Undefined variable: _tableName in /var/www/html/lib/Local/Models/Payments.php on line 13 when I don't set it as a parameter. and an error Cannot redeclare static XXX\Model::$_tableName when I do.
UPDATE
When I try to use the find method with this abstract Model, it's not setting the tableName;
public static function find($idOrWhere = false, $params = array(), $limit = false)
{
$sql = "SELECT * FROM " . static::tableName();
I don't know how to set that now. It just ignores what I have put in my class.
You have to remove the $ when accessing a class property:
class Payments extends Model
{
public function __construct()
{
$this->_tableName = 'payments';
}
}
Indeed this is irritating, but that's the way php syntax works.
With static class you need to use the self keyword to initialize property in class:
class Foo {
static $bar;
}
Foo::$bar = array(…);
or
class Foo {
private static $bar;
static function init()
{
self::$bar = array(…);
}
}
Foo::init();
Related
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
Here is an abstract class I have to use;
abstract class Model
{
protected static $_tableName = false;
public static function tableName()
{
return static::$_tableName;
}
public static function find($idOrWhere = false, $params = array(), $limit = false)
{
$sql = "SELECT * FROM " . static::tableName();
I can't seem to set the _tableName, static::tableName(), or tableName() in my own class;
class Payments extends Model {
public function __construct()
{
$this->_tableName = 'payments';
}
That's not doing anything! It's not set the tableName to payments. And I can't figure out how to use the method tableName() either.
You are trying to access _tableName in a non-static way (I.e. $this->_tablename) even though you've declared it as static.
You need to access it like so:
self::$_tableName
OR, for late static binding:
static::$_tableName
Overall, you should avoid the use of static classes as much as possible, mainly due to testing purposes.
Static members have to be accessed on the class, not on an instance:
class Payments extends Model {
public function __construct()
{
Payments::$_tableName = 'payments';
}
}
A property declared as static cannot be accessed with an instantiated class object (though a static method can).
Static properties cannot be accessed through the object using the arrow operator ->.
http://php.net/manual/en/language.oop5.static.php
Like any other PHP static variable, static properties may only be initialized using a literal or constant; expressions are not allowed.
According to this material your classes should be reworked that way.
abstract class Model
{
protected static $_tableName = false;
public static function find($idOrWhere = false, $params = array(), $limit = false)
{
$sql = "SELECT * FROM " . self::tableName();
...
}
private static function tableName()
{
if (!static::$_tableName) {
throw new \RuntimeException('No table name provided');
}
return static::$_tableName;
}
class Payments extends Model {
protected static $_tableName = 'payments';
}
Btw, __contruct is called on object instantiation and you should not set any static properties values there.
I have class DbTable, which implements all db queries to database such as insertRecord, updateRecord, ... But variable is not rewriting.
abstract class DbTable {
public static $table;
public static function insertRecord($data) {
// here I add some values to data, but that's not important
my_db::insert(self::$table, $data);
}
}
class User extends DbTable {
public static $table = 'table_users';
}
// everywhere I can call
User::insertRecord($data);
I know I can call
$c = get_called_class();
my_db::insert($c::$table, $data);
but I think that's not best solution at all.
Method and variables can be non static, I just use them because it is comfortable to write User::insertRecord instead of $user = new User(); $user->insertRecord($data);
When you're working with static classes you need to specify your variable source, in this case you're scoping to both classes and not on single class, this makes a difference, because self is scoping to concurrent class and when you want to scope for both classes you have to use static.
/**
* For testing
*/
class my_db {
public static function insert($table, $data){
echo $table;
}
}
abstract class DbTable {
public static $table = null;
public static function insertRecord($data) {
//self::$table is empty
//static::$table has 'table_users'
// here I add some values to data, but that's not important
my_db::insert(static::$table, $data);
}
}
class User extends DbTable {
public static $table = 'table_users';
}
// everywhere I can call
User::insertRecord(['Hi']);
self::$table is empty
static::$table has 'table_users'
You can read more about this here: SO Answer and PHP Documentation
Use static variables are unnecessary in this case. You just need dynamically create User object and them call method.
abstract class DbTable
{
protected $tableName;
public static function insertRecord($data)
{
$object = static::newInstance();
$object->insert($data);
}
public static function newInstance()
{
$className = get_called_class();
return new $className();
}
public function insert($data)
{
my_db::insert($this->tableName, $data);
}
}
class User extends DbTable
{
public function __construct()
{
$this->tableName = 'table_users';
}
}
You can now call:
User::insertRecord(['col1' => 'val1']);
But also you can insert rows from instated object:
$user = new User();
$user->insert(['col1' => 'val1']);
So, this is my trait:
trait Cacheable
{
protected static $isCacheEnabled = false;
protected static $cacheExpirationTime = null;
public static function isCacheEnabled()
{
return static::$isCacheEnabled && Cache::isEnabled();
}
public static function getCacheExpirationTime()
{
return static::$cacheExpirationTime;
}
}
This is the base class:
abstract class BaseClass extends SomeOtherBaseClass
{
use Cacheable;
...
}
These are my 2 final classes:
class Class1 extends BaseClass
{
...
}
class Class2 extends BaseClass
{
protected static $isCacheEnabled = true;
protected static $cacheExpirationTime = 3600;
...
}
Here is the part of the code which executes these classes:
function baseClassRunner($baseClassName)
{
...
$output = null;
if ($baseClassName::isCacheEnabled()) {
$output = Cache::getInstance()->get('the_key');
}
if ($output === null) {
$baseClass = new $baseClassName();
$output = $baseClass->getOutput();
if ($baseClassName::isCacheEnabled()) {
Cache::getInstance()->set('the_key', $output);
}
}
...
}
This code doesn't work because PHP complains about defining same properties in Class2 as in Cacheable. I can't set them in their constructors because I want to read them even before running the constructor. I'm open for ideas, any help would be appreciated. :)
EDIT:
Well, I use this Cacheable trait on several places so i kind of got mixed up. :) This works fine like this. But I have another class which directly uses the Cacheable trait and when I try to do this on that class, I get the metioned error. So... Just assume that the BaseClass isn't abstract and I'm trying to set these cache properties on it. The question remains the same.
You can not reassign trait properties.
From PHP manual http://php.net/traits
See Example #12 Conflict Resolution
If a trait defines a property then a class can not define a property
with the same name, otherwise an error is issued. It is an E_STRICT if
the class definition is compatible (same visibility and initial value)
or fatal error otherwise.
One solution would be to define override properties in the class
class Class2 extends BaseClass
{
protected static $_isCacheEnabled = true;
protected static $_cacheExpirationTime = 3600;
...
}
and then modify your trait as such...
trait Cacheable
{
protected static $isCacheEnabled = false;
protected static $cacheExpirationTime = null;
public static function isCacheEnabled()
{
if ( Cache::isEnabled() ) {
return isset( static::$_isCacheEnabled ) ? static::$_isCacheEnabled :
static::$isCacheEnabled;
} else {
return false;
}
}
public static function getCacheExpirationTime()
{
return isset ( static::$_cacheExpirationTime ) ? static::$_cacheExpirationTime :
static::$cacheExpirationTime;
}
}
You cannot override properties, but you can override functions. So one of the possible solutions, if you're going to use the properties as given, not changing them, could be:
trait Cacheable {
protected static function isCacheEnabledForClass() { return false; }
public static function isCacheEnabled()
{
return static::isCacheEnabledForClass() && Cache::isEnabled();
}
}
class Class2 extends BaseClass {
protected static function isCacheEnabledForClass() { return true; }
}
You could use defined():
// only defined in classes
// static $isCacheEnabled = false;
public static function isCacheEnabled()
{
return defined(static::$isCacheEnabled ) ? static::$isCacheEnabled : false;
}
Or maybe you could live with the variable being protected instead of static?
I have a simple question. I use a singleton which implements an abstract class. Is it possible to put the getInstance() Method and the variable $_instance in the abstract class instead of the concrete one I want to create?
Here's my code:
<?php
class Command_Log extends Command_Abstract {
private static $_instance=null;
public static function getInstance() {
if (self::$_instance==null)
self::$_instance=new self();
return self::$_instance;
}
protected function realExecute() {
}
protected function realSimulate($fileHandle) {
}
}
and
<?php
abstract class Command_Abstract implements Command_Interface {
protected $_data=array();
//private static $_instance=null;
protected $_isExecuted=false;
protected $_execute=false;
public function enableExecute() {
$this->_execute=true;
return $this;
}
protected function __construct() {
}
protected function __clone() {}
public function addData($data) {
array_push($this->_data,$data);
return $this;
}
abstract protected function realExecute();
abstract protected function realSimulate($fileHandle);
public function execute() {
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$this->realExecute();
}
}
public function simulate() {
$exitSystem = false;
if(!$this->_isExecuted && $this->_execute) {
$this->_isExecuted = true;
$exitSystem = $this->realSimulate($fh);
}
}
return $exitSystem;
}
}
I have many implementations of the the commands, so I don't want redundant code everywhere in my implementations. Is it possible to put these two things in the abstract class, if yes please tell me how.
If not please explain it to me why it isnt possbile. Or if I need to change something to do it, anyhow.
regards
YES WE CAN!
I have a class called Singleton that is abstract... and many classes that extends that Singleton class... this is the code:
abstract class Singleton {
private static $instances = array();
final private function __construct($_params) {
$class = get_called_class();
if (array_key_exists($class, self::$instances))
throw new Exception('An instance of '. $class .' already exists !');
//static::initialize(); //In PHP 5.3
$this->initialize($_params);
}
final private function __clone() { }
abstract protected function initialize();
public static function getInstance($_params=array()) {
$class = get_called_class();
if (array_key_exists($class, self::$instances) === false){
self::$instances[$class] = new $class($_params);
}
return self::$instances[$class];
}
}
and (for example) the class DBConnection that extends from Singleton
class DBConnection extends Singleton{
private $connexion_pdo=null;
protected function initialize(){
//connect to the DB
$this->connexion_pdo = blablalba;
}
}
although there are some problems in php 5.2.. specially with the function get_called_class() and the static::initialize()
You could also check the php site for patterns... there are lots of contributions for the singleton
Good Luck