PHP OOP Cannot set private properties - php

I have a class UserFavorite that extends User.
When i create new object with the class constructor setters cannot set properties.
Constructor:
public function __construct($username, $tabId, $favName, $favUrl = null, $favPosition = null, $favComment = null) {
parent::__construct($username);
$this->tabId = $this->setTabId($tabId);
$this->favName = $this->setFavName($favName);
$this->favUrl = $this->setFavUrl($favUrl);
$this->favPosition = $this->setFavPosition($favPosition);
if ($favComment) {
$this->favComment = $this->setFavComment($favComment);
}
}
Setter:
public function setFavUrl($favUrl) {
$url = filter_var($favUrl, FILTER_VALIDATE_URL);
if (!$url) {
echo $this->showError(...);
exit;
}
echo $url; // THIS LOGS THE URL
$this->favUrl = $url;
}
I creatirng new instance $fav = new UserFavorite($user->getUsername(), 1, 'favorite', 'http://abv.bg', 5, 'mamatisiebalo' );
And when i print $fav i receive :
favorite<pre>UserFavorite Object
(
[favName:UserFavorite:private] =>
[tabId:UserFavorite:private] =>
[favUrl:UserFavorite:private] =>
[favPosition:UserFavorite:private] =>
[favComment:UserFavorite:private] =>
[_favId:UserFavorite:private] =>
[username:protected] => myUserName
[_userId:protected] => 1
)
Any ideas?

You are setting $this->favUrl in the setter function, then overwritting it by assigning the result of the setter function to the same variable.
If you change
$this->favUrl = $this->setFavUrl($favUrl);
To
$this->setFavUrl($favUrl);
You should be OK.

Related

Use Trait Function with Same Name but Optionally

PHP Class Using Same Name as Trait Function
Refer to the question I just asked above here. Here was my original code.
trait sampletrait{
function hello(){
echo "hello from trait";
}
}
class client{
use sampletrait;
function hello(){
echo "hello from class";
//From within here, how do I call traits hello() function also?
}
}
I can call the trait function like this thanks to the answer to the question.
class client{
use sampletrait {
hello as protected sampletrait_hello;
}
function hello(){
$this->sampletrait_hello();
echo "hello from class";
}
}
My question is if my class client did not have a function hello() but wanted to call it is this possible?
So for example...
trait sampletrait{
function hello(){
echo "hello from trait";
}
}
class client{
use sampletrait {
hello as protected sampletrait_hello;
}
}
I'm aware that I could just simply say use sampletrait; and it would have the function but in my use case I can't do that either. Is it possible to have the aliased name but still use the trait name default if it does not exist in the class?
Extra Information
My exact use case involves PHP-ActiveRecord
I have a trait called uniquecheck
trait uniquecheck {
//#JA - Used temporarely to determine if editing for the unique checker
static $isEditing = false;
//#JA - This is used by PHPActiveRecord to trigger events before validation on update calls only.
static $before_validation_on_update = array('before_validation_on_update_callback');
//#JA - This is function used as callback from PHPActiveRecord
public function before_validation_on_update_callback(){
self::$isEditing = true; //#JA - Requires Uniquecheck trait to work
}
//#JA - This function can do single and multi-unique checks.
//#JA - This is programmed to be replaced at a later date when validates_uniqueness_of is fixed (http://www.phpactiverecord.org/projects/main/wiki/Validations#validates_uniqueness_of)
//#JA - EXAMPLES
//SINGLE -- array('name','message' => 'Can't do this')
//MULTIPLE -- array( array('name1','name2'), 'message' => 'can't do this and that together')
//#JA - To be clear multiple does not mean 2 different uniques but a unique on 2 columns. Just use this function twice for 2 separate unique checks.
public function uniquecheck($rules = array()) {
$classname = get_class($this);
//#JA - Basic validation to confirm assumptions for function properties
if(count($rules)<=0){
die('uniquecheck.php -> Property array can not be empty');
}
//#JA - If its an array use the MULTIPLE method
if(is_array($rules[0])){
//#JA - First create the condition string
$conditionstring = '';
$conditionarray = array();
$uniques = $rules[0];
foreach($uniques as $unique){
$conditionstring .= "$unique = ? AND ";
}
$conditionstring = substr($conditionstring, 0, -5);
//#JA - Then generate the array we will use for the conditions
$conditionarray['conditions'][] = $conditionstring;
foreach($uniques as $unique){
$conditionarray['conditions'][] = $this->read_attribute($unique);
}
$results = $classname::find('all',$conditionarray);
if($classname::$isEditing == true){
die('was editing');
}else{
die('was creating');
}
//#JA - If in edit mode, if the values are exactly the same as it was before then ignore this check.
if (count($results)>=1) {
foreach($uniques as $unique){
$this->errors->add($unique, $rules['message']);
}
}
}else{ //#JA - Otherwise use the SINGLE method
$unique = $rules[0];
$results = $classname::find('all',array('conditions' => array("$unique = ?", $this->read_attribute($unique))));
//#JA - If there is more then 1 result then its not unique!
if (count($results)>=1) {
$this->errors->add($unique, $rules['message']);
}
}
}
}
?>
I use this in my model Client like so...
class Client extends ActiveRecord\Model {
use foreignkeycheck;
use uniquecheck {
before_validation_on_update_callback as protected uniquecheck_before_validation_on_update_callback;
}
static $before_destroy = array('before_destroy_callback');
//#gv hide columns that are not in use right now
static $columnsToHide = array(
'affiliate_code',
'autopay',
'stripe_customer_id',
'quickbooks_client_id',
'stripe_customer_info',
'stripe_customer_info_last_update',
'textingnumber'
);
static $easy_name = "Client";
static $validates_presence_of = array(
array('clienttype_id'),
array('company_id'),
array('contactfirstname'),
array('contactlastname'),
array('contactphonenumber')
);
static $validates_size_of = array(
array('contactfirstname', 'within' => array(1, 50)),
array('contactlastname', 'within' => array(1, 50)),
array('contactaddress', 'within' => array(1, 120), 'allow_null' => false),
array('companyaddress', 'within' => array(1, 120), 'allow_null' => true),
array('companyname', 'within' => array(1, 75), 'allow_null' => true),
);
// static $validates_uniqueness_of = array(
// array('affiliate_code', 'allow_null' => true),
// array(array('contactfirstname', 'contactlastname', 'contactemail', 'contactphonenumber', 'contactaddress'),
// 'message' => 'Can\'t have duplicate client.')
// );
static $validates_format_of = array(
array('contactemail', 'with' => '/\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,10}\b/sim',
'message' => 'Must be a correctly formatted email.', 'allow_blank' => true, 'allow_null' => true),
array('companyemail', 'with' => '/\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,10}\b/sim',
'message' => 'Must be a correctly formatted email.', 'allow_blank' => true, 'allow_null' => true),
array('companyphonenumber', 'with' => '/^(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}$/i',
'message' => 'Phone number is invalid', 'allow_blank' => true, 'allow_null' => true),
array('contactphonenumber', 'with' => '/^(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}$/i',
'message' => 'Phone number is invalid', 'allow_blank' => true, 'allow_null' => false)
);
//This allows you to use your own as well as still call the uniquechecks before_validation callback in case this method is not needed.
public function before_validation_on_update_callback(){
$this->uniquecheck_before_validation_on_update_callback();
}
public function before_destroy_callback(){
$conn = SELF::connection();
$conn->transaction();
try {
//USER *********
//Delete the associated user as well.
$related_users = User::find('all',array(
'conditions' => array(
'client_id' => $this->id)
));
foreach($related_users as $user){
$user->delete();
}
//PROPERTIES ********
//Delete all properties of the client, which in turn delets all routes & visits
$related_properties = Property::find('all',array(
'conditions' => array(
'client_id' => $this->id)
));
foreach($related_properties as $property){
$property->delete();
}
//Only have to delete the user, because deletes will cascade down
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
}
return true; //will actually delete the client now.
}
public function validate() {
//Thought about putting user validation in here, but decided against it.
//Multi-unique check FAILS to work if the parameter is not passsed for one of the multi-uniques. This is BUG in PHP Active Record.
//Does not show message correctly for multi-uniques either. This is ALSO a bug in PHP Active Record.
//#JA - Uses multi-unique check. Its only not allowed if all 4 of these values are the same since its obviously duplicate at that point
$this->uniquecheck(array(array('company_id','contactfirstname', 'contactlastname', 'contactphonenumber', 'contactaddress'),'message' => 'Can\'t have duplicate client.'));
$this->foreignkeycheck('Clienttype');
$this->foreignkeycheck('Company');
$this->foreignkeycheck('Affiliate', 'affiliate_code', true); //Special case where foreign key is not _id, true sent to indicate validate is optional only if a value is not null.
}
public function getReadableColumnNames($flip = false) {
$readableColumns = array();
$readableColumns["contactfirstname"] = "First Name";
$readableColumns["contactlastname"] = "Last Name";
$readableColumns["contactphonenumber"] = "Phone Number";
$readableColumns["contactemail"] = "Email";
$readableColumns["contactaddress"] = "Address";
$readableColumns["companyaddress"] = "Company Address";
$readableColumns["companyemail"] = "Company Email";
$readableColumns["companyname"] = "Company Name";
$readableColumns["companyphonenumber"] = "Company Phone #";
$readableColumns["affiliate_code"] = "Affiliate #";
$readableColumns["clienttype_id"] = "Client Type";
$readableColumns["company_id"] = "Company";
$readableColumns["stripe_customer_id"] = "Stripe Customer ID";
$readableColumns["stripe_customer_info"] = "Stripe Customer Info";
$readableColumns["stripe_customer_info_last_update"] = "Stripe Info Last Update";
$readableColumns["welcome_email_sent"] = "Welcome Email Sent?";
$readableColumns["autopay"] = "Auto Pay?";
$readableColumns["active"] = "Active?";
if ($flip == true) {
$readableColumns = array_flip($readableColumns); //swap keys and values~
}
return $readableColumns;
}
public function getDefaultColumns() {
$defaultColumns = array();
$defaultColumns[] = "contactfirstname"; //first sort order
$defaultColumns[] = "contactlastname"; //second sort order
$defaultColumns[] = "contactphonenumber";
$defaultColumns[] = "contactemail"; //etc...
return $defaultColumns;
}
public function getColumnExceptions() {
$tableNames = array();
return $tableNames;
}
public function getBatchActions() {
$batchActions = array();
//$batchActions['Text to Appear'] = 'ClassName'
//For JS File To Call Correct Function ^^^^
//Order of array determines order in respective dropdown menu.
$batchActions["Make Inactive"] = "batch_make_inactive";
$batchActions["Send Email"] = "batch_send_email";
$batchActions["Send Welcome Email"] = "batch_send_client_welcomeEmail";
return $batchActions;
}
public function getRowActions() {
$rowActions = array();
$rowActions["Edit"] = array("edit_typename", true); //Call generic typename edit function, true means this is the item that shows first.
$rowActions["View Pictures"] = array("view_pictures_for_client", false); //shortcut to prefill information for property~
$rowActions["Add Property"] = array("add_property_for_client", false); //shortcut to prefill information for property~
//$rowActions["Update Quickbooks"] = array("qb_update_customer", false); //shortcut to add customer to quickbooks if connected.
$rowActions["Create User ID"] = array("create_userid_for_client", false); //shortcut method to create user_id straight from the client~
$rowActions["Send Welcome Email"] = array("send_client_welcome_email", false);
$rowActions["Make Inactive"] = array("allinactive_client", false); //will make the user inactive, property and user_id, along with recurring invoices, estimates, invoices that were referenced by client.
$rowActions["Make Active"] = array("allactive_client", false);
$rowActions["Delete"] = array("delete_typename", false); //call to generic typename delete function
//#gv Functions that do not work and not part of Release 1.0
//$rowActions["Add Estimate"] = array("add_estimate_for_client",false); //shortcut to prefill information for property~
//$rowActions["Add Invoice"] = array("add_invoice_for_client",false); //shortcut to prefill information for property~
//$rowActions["Add To Quickbooks"] = array("qb_add_customer",false); //shortcut to add customer to quickbooks if connected.
//$rowActions["Make Inactive"] = array("inactive_typename",false); //Way to filter results if you desired by clients that are not relevant anymore.
//$rowActions["Send Email"] = array("send_client_email",false);
//$rowActions["Send Text"] = array("text_client",false);
return $rowActions;
}
public function getColumnInterestedColumns() {
$columnInterestedColumns = array();
$columnInterestedColumns["clienttype_id"] = array("name");
$columnInterestedColumns["company_id"] = array("companyname");
$columnInterestedColumns["client_id"] = array("contactfirstname", "contactlastname"); //external reference.
return $columnInterestedColumns;
}
//This function indicates to the UI what fields are dependent upon others for purpose of 'flow' for new and edit areas.
//Happens in 2 areas, on initial PHP creation uses this to hide the field, and upon the restricted fields parent values taking on a value or losing a value.
public function getColumnRestrictions() {
global $user;
$restrictedColumns = array();
//$restrictedColumns["property_id"] = array("client_id");//this means that property_id can not show in UI until client_id is set.
return $restrictedColumns;
}
}
?>
I am trying to use this to get around phpactiverecords unique check bug since it does not work in there system for a project I'm working on.
It uses a callback like this (before_validation_on_update_callback), where it has to have that name.
I wanted to use a trait to include it in all my models for unique checking easily.
Refer to this (http://www.phpactiverecord.org/projects/main/wiki/Callbacks)
Try setting public on the trait function and then protected when you rename
trait sampletrait{
public function hello(){
echo "hello from trait";
}
}
class client{
use sampletrait {
hello as protected sampletrait_hello;
}
}
$c = new client();
$c->hello();
As said here PHP Class Using Same Name as Trait Function both hello and sampletrait_hello will exist, but as hello is public and sampletrait_hello protected only hello will be callable from an outer scope.
And if you overwrite hello, you will be able to call sampletrait_hello inside it.

Generating a php object, two levels deep

I'm new to php - objects and arrays, especially. Coming from a JavaScript world, I'm having a modicum of trouble understanding the right way to construct objects, that may easily be iterated.
I'd like to create an object (or array - although I suspect an object would be more suitable) with the following structure:
$client_body:
$cst:
$title: 'Unique string'
$copy: function_result()
$ser:
$title: 'Unique string'
$copy: function_result()
$imp
$title: 'Unique string'
$copy: function_result()
...
I've been trying with variations on the following, but with numerous errors:
$client_body = new stdClass();
$client_body->cst->title = 'Client case study';
$client_body->cst->copy = get_field('client_cst');
$client_body->ser->title = 'Our service';
$client_body->ser->copy = get_field('client_ser');
...
And it seems that, using this approach, I'd have to use a new stdClass invocation with each new top-level addition, which seems a little verbose.
Could someone point me in the right direction?
You can just typecast an array to an object:
$client_body = (object)array(
"cst" => (object)array(
"title" => "Unique string",
"copy" => function_result()
)
);
You can try this object class more OOP:
<?php
class ClientBody{
protected $cst;
protected $ser;
protected $imp;
public function __construct($cst = '', $ser ='', $imp = '')
{
$this->cst = $cst;
$this->ser = $ser;
$this->imp = $imp;
}
public function getCst()
{
return $this->cst;
}
public function getSer()
{
return $this->ser;
}
public function getImp()
{
return $this->imp;
}
public function setCst($value)
{
$this->cst = $value;
}
public function setSer($value)
{
$this->ser = $value;
}
public function setImp($value)
{
$this->imp = $value;
}
}
$myObject = new ClientBody('toto', 'titi', 'tata');
echo $myObject->getCst(); // output 'toto'
echo $myObject->getSer(); // output 'titi'
echo $myObject->getImp(); // output 'tata'
Or you could use json_decode($client_body, TRUE);

prevent parent class 'forgetting' local vars set by children: php

I have this class
class Validator implements iValidation{
protected
$_fields,
$_errors;
public function __construct($fields){
$this->_errors = array();
$this->_fields = $fields;
}
public function validate(){
$map = unserialize( iValidation::map );
foreach ($this->_fields as $field_type => $data){
if ( array_key_exists( $field_type, $map ) ){
$class = "Validate_{$map["$field_type"]}" ;
$object = new $class($data);
$object->validate();
unset($object);
}
}
}
public function getErrors(){
return $this->_errors;
}
}
Now this class loops through an array given in the format
$admin_test_data = array(
"firstname" => "Alex1",
"surname" => "Morley-Finch",
"username" => "alex123",
"password" => "cheese",
"re_password" => "cheese",
"email_address" => "alex54353hotmail.co.uk",
"user_type" => "ADMIN",
"add_admin" => "Add Admin",
);
I have a map that describes the type of validation on each field that is declared like so:
define(
"VALIDATION_MAP",
serialize(
array(
// FIELD => VALIDATION TYPE
"firstname" => "Name",
"surname" => "Name",
"agency_name" => "Agency_Name",
"agency_office" => "Name",
"username" => "Username",
"email_address" => "Email_Address",
"password" => "Password",
)
)
);
interface iValidation{
public function __construct($data);
public function validate();
const map = VALIDATION_MAP;
}
And I have sub classes One of which is like this:
class Validate_Name extends Validator implements iValidation{
private $_data;
public function __construct($data){
$this->_data = $data;
}
public function validate(){
$data = $this->_data;
$length = strlen($data);
if ( $length > 40 ){
$this->_errors[] = "Cannot be more than 40 characters";
}
if ( $length < 3 ){
$this->_errors[] = "Cannot be less than 3 characters";
}
if ( !preg_match("/^[a-zA-Z]+$/", $data) ){
$this->_errors[] = "A-Z characters only";
}
}
}
And the whole framework is used like so:
$validator = new Validator($admin_test_data);
$validator->validate(); // ^ defined earlier ^^^
$errors = $validator->getErrors();
Now after all that background info... lets get to the problem.
I'm probably doing some EXTREMELY stupid but I can't figure it out and Google isn't doing a good job of enlightening me ( or I'm not doing a good job of asking google )
The sub classes reference $this->_errors[] = "This is the error"; and when using die( var_dump( $this->_errors ) ); inside the sub class, the errors for that class appear as they should.
However if I call die( var_dump( $validator->getErrors() ) ); outside the classes, the array is empty.
Asif the parent has forgotten all the values the children set?
Whats going on?
For each field you validate in Validator, you create an instance of the required implementation of iValidation.
Now the child validator validates, and it finds errors. It adds those errors to its _errors instance attribute.
When it's done validating, the child instance is discarded. And that's where the problem lies, since the errors found were assigned to the child instance's _errors attribute, not that of the parent instance.
Nothing ever gets assigned to Validator::_errors, because those properties are in different objects.

How to create nested PHP value object? (class)

I would like to create the following using class syntax:
$resp = new stdclass;
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
Should be easy but I cannot find the syntax for this.
I will later output $resp in json format. I am aware I can also use arrays for this...
The json output shall be:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
You can also make your classes more explicit:
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
}
class CategoryListResp {
public $category = array();
}
$resp = new Resp();
$resp->categoryListResp->category[0]->categoryId = 1;
$resp->categoryListResp->category[0]->categoryName = "Spel";
$resp->categoryListResp->category[0]->iconUri = "PictoSpel.png";
// etc.
ADDED (based on henq's comment). To fully utilize the class concept you would need to add some methods to the classes. Then you would not use -> for arrays, but call the respective methods. E.g.
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
public function __construct($id, $name, $icon) {
$this->categoryId = $id;
$this->categoryName = $name;
$this->iconUri = $icon;
}
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
public function addCategory($index, $id, $name, $icon) {
$this->categoryListResp->addCategory($index, $id, $name, $icon);
}
}
class CategoryListResp {
public $category = array();
public function addCategory($index, $id, $name, $icon) {
$this->category[$index] = new Category($id, $name, $icon);
}
}
$resp = new Resp();
$resp->addCategory(0, 1, "Spel", "PictoSpel.png");
$resp->addCategory(1, 2, "Transport", "PictoTransport.png");
// etc
You can modify this concept according to your needs.
You're almost there already:
$resp = new stdClass();
$resp->CategoryListResp = new stdClass();
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
print_r(json_encode($resp));
/*
output:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
*/
Just send $resp to json_encode. Your code should work as is, however. It's better design to create class definitions for CategoryListResp and Category, rather than just using stdClass.
Arrays are the simpler way to go (as suggested by #felix-kling)
This is how the code ended up:
$resp = array(
'CategoryListResp' => array(
'category' => array(
array(
'categoryId' => 1,
'categoryName' => 'Spel',
'iconUri' => 'PictoSpel.png'
),
array(
'categoryId' => 2,
'categoryName' => 'Transport',
'iconUri' => 'PictoTransport.png'
),
),
),
);
print json_encode($resp);
Clean and simple.

jQuery style Constructors in PHP

Is there a way to instantiate a new PHP object in a similar manner to those in jQuery? I'm talking about assigning a variable number of arguments when creating the object. For example, I know I could do something like:
...
//in my Class
__contruct($name, $height, $eye_colour, $car, $password) {
...
}
$p1 = new person("bob", "5'9", "Blue", "toyota", "password");
But I'd like to set only some of them maybe. So something like:
$p1 = new person({
name: "bob",
eyes: "blue"});
Which is more along the lines of how it is done in jQuery and other frameworks. Is this built in to PHP? Is there a way to do it? Or a reason I should avoid it?
the best method to do this is using an array:
class Sample
{
private $first = "default";
private $second = "default";
private $third = "default";
function __construct($params = array())
{
foreach($params as $key => $value)
{
if(isset($this->$key))
{
$this->$key = $value; //Update
}
}
}
}
And then construct with an array
$data = array(
'first' => "hello"
//Etc
);
$Object = new Sample($data);
class foo {
function __construct($args) {
foreach($args as $k => $v) $this->$k = $v;
echo $this->name;
}
}
new foo(array(
'name' => 'John'
));
The closest I could think of.
If you want to be more fancy and just want to allow certain keys, you can use __set() (only on php 5)
var $allowedKeys = array('name', 'age', 'hobby');
public function __set($k, $v) {
if(in_array($k, $this->allowedKeys)) {
$this->$k = $v;
}
}
get args won't work as PHP will see only one argument being passed.
public __contruct($options) {
$options = json_decode( $options );
....
// list of properties with ternary operator to set default values if not in $options
....
}
have a looksee at json_decode()
The closest I can think of is to use array() and extract().
...
//in your Class
__contruct($options = array()) {
// default values
$password = 'password';
$name = 'Untitled 1';
$eyes = '#353433';
// extract the options
extract ($options);
// stuff
...
}
And when creating it.
$p1 = new person(array(
'name' => "bob",
'eyes' => "blue"
));

Categories