Use Trait Function with Same Name but Optionally - php

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.

Related

How dou you write/call 2 tables in $this->db->table('table_name') in Codeigniter 4

Hello i have 2 tables that i want to call right now, for the EDIT (part of the CRUD)
tables:
table_a
table_b
i found in youtube how to update/edit from 2 tables, i need to call bot of the tables.
here's the code for the model
public function edit_this($ID_A)
{
return $this->db->table('table_a', '*i don't know how to insert the 2nd table')->where('ID_A', $ID_A)->get()->getRowArray();
}
Here's the controller
public function this_edit($ID_A)
{
$data = [
'title' => 'Admin',
'navbartitel' => 'You know this',
'alledit' => $this->theModel->edit_this($ID_A),
'validation' => \Config\Services::validation()
];
return view('this/all/edit', $data);
}
it works but i only can accsess the tabel_a, but i need them both so i can show what i've written in the edit form, from the database
anyone can help? thank you
$this->db->table(...) returns an instance of QueryBuilder and will happily accept a single string of comma-separated tables ("table1, table2..."), or even an array for that matter (['table1', 'table2'...]), as its first parameter. You are doing neither and instead passing multiple parameters.
When you call table(), the value passed in the first parameter is used during the creation of the database-specific Builder class:
public function table($tableName)
{
if (empty($tableName))
{
throw new DatabaseException('You must set the database table to be used with your query.');
}
$className = str_replace('Connection', 'Builder', get_class($this));
return new $className($tableName, $this);
}
The DB-specific Builder class has no constructor of its own so falls back on the __construct defined in BaseBuilder, which it extends:
public function __construct($tableName, ConnectionInterface &$db, array $options = null)
{
if (empty($tableName))
{
throw new DatabaseException('A table must be specified when creating a new Query Builder.');
}
$this->db = $db;
$this->from($tableName);
...
I've truncated this for brevity because the important part is that call to $this->from, which is in the end how multiple tables get processed:
public function from($from, bool $overwrite = false)
{
if ($overwrite === true)
{
$this->QBFrom = [];
$this->db->setAliasedTables([]);
}
foreach ((array) $from as $val)
{
if (strpos($val, ',') !== false)
{
foreach (explode(',', $val) as $v)
{
$v = trim($v);
$this->trackAliases($v);
$this->QBFrom[] = $v = $this->db->protectIdentifiers($v, true, null, false);
}
}
else
{
$val = trim($val);
// Extract any aliases that might exist. We use this information
// in the protectIdentifiers to know whether to add a table prefix
$this->trackAliases($val);
$this->QBFrom[] = $this->db->protectIdentifiers($val, true, null, false);
}
}
return $this;
}

Validating POST data class

This is an extension of a question I asked earlier that was deemed to be unsafe practise, due to the use of eval(). So I went for another approach but I have run into a problem. I do not know how to convert it to a class. My attempt ends with an error when I try to use call_user_func_array. it can't find the function in the class. Can you give me some hint so I get going? Thanks!
The error message I get when I try to run my code is Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'testlength' not found or invalid function name but on all validation methods. This is what I don't understand. This is what I want help to understand why it does not work.
class ruleValidator
{
protected $postData = array();
protected $ruleSet = array();
var $exceptions = 'Å,Ä,Þ,å,ä,þ,Ø,Ö,Ð,ø,ö,ð,Æ,Ü,æ,ü,á,é,í,ñ,ó,ú,ü,Á,É,Í,Ñ,Ó,Ê,Ú,Ü,ß';
function __construct(){
$this->exceptions = explode(',',$exceptions);
}
function testlength($string,$threshold)
{
return strlen($string)<$threshold?
'Your %s is too short.': // TRUE
''; // FALSE
}
function testnumeric($string,$offset,$length,$switch=true)
{
if(is_numeric(substr($string,$offset,$length))===$switch)
{
return $switch?
'Your %s has to begin with a character.': // Truely TRUE
'Your %s is containing non numeric characters. Please enter only digits.'; // Falsely TRUE
}
}
function testemail($string)
{
return filter_var($string, FILTER_VALIDATE_EMAIL)?
'': // TRUE
'Your email is not in a valid form.'; // FALSE
}
function testpattern($string,$pattern='/^[0-9]{8,10}$/')
{
return preg_match($pattern, $string)?
'': // TRUE
'Your %s is entered incorrect. Please use the correct format when entering data.'; // FALSE
}
function testequalto($string1,$string2)
{
return $string1==$string2?
'': // TRUE
'Your %s fields do not match eachother.'; // FALSE
}
function testchecked($bool)
{
return $bool===true?
'': // TRUE
'You are required to check this %s to continue.'; // FALSE
}
function testspecchar($string,$excludes=array())
{
if(is_array($excludes)&&!empty($excludes))
{
foreach($excludes as $exclude)
{
$string=str_replace($exclude,'',$string);
}
}
if(preg_match('/[^a-z0-9 ]+/i',$string))
{
return 'Your %s contains illegal characters.'; // TRUE
}
return; // FALSE
}
}
This is an array with how the POST data is recieved in the validator and the rules I use for the different fields in the form.
$exceptions = explode(',','Å,Ä,Þ,å,ä,þ,Ø,Ö,Ð,ø,ö,ð,Æ,Ü,æ,ü,á,é,í,ñ,ó,ú,ü,Á,É,Í,Ñ,Ó,Ê,Ú,Ü,ß');
$postData = array
(
'name' => 'Mikael',
'familyname' => 'Eriksson`',
'username' => 'Mik',
'password' => 'testtest',
'password-confirm' => 'testtesty',
'email' => 'try.to#guess.it,se',
'phone' => '0000000000a',
'policy' => 0
);
$ruleSet = array
(
'name'=>array
(
'testlength'=>2,
'testnumeric'=>array(0,1),
'testspecchar'=>array($exceptions)
),
'familyname'=>array
(
'testlength'=>2,
'testnumeric'=>array(0,1),
'testspecchar'=>array($exceptions)
),
'username'=>array
(
'testlength'=>4,
'testnumeric'=>array(0,1),
'testspecchar'=>array()
),
'email'=>array
(
'testemail'=>array()
),
'phone'=>array
(
'testnumeric'=>array(0,strlen($postData['phone']),false),
'testpattern'=>'/^[0-9]{8,10}$/'
),
'password'=>array
(
'testlength'=>8
),
'password-confirm'=>array
(
'testequalto'=>$postData['password-confirm']
),
'policy'=>array
(
'testchecked'=>array()
)
);
Here is how I validated the data up until now. It works, but I want to make this to a class to streamline the code in my project.
foreach($postData as $key => $value)
{
if(!array_key_exists($key,$ruleSet))
{
$errors[] = "The field `$key` is not part of the form. Only send actual form data.";
break;
}
$slice = array($key=>$ruleSet[$key]);
foreach($slice as $rules => $rule)
{
foreach($rule as $rls => $r)
{
$r = array_merge((array)$value,(array)$r);
$errors[] = sprintf(call_user_func_array($rls,$r),$key);
}
}
}
if(count($errors)>0) return implode(';;',array_filter($errors,'strlen'));
When you want to call a method of a class, you have to make an instance (using new) or call them statically when the methods are declared static.
In both ways, you have to tell call_user_func_array() that you are not calling a function in the global scope, but from within a class.
call_user_func_array(array('ruleValidator', $rls), $r)
Then declare the functions static:
public static function testlength($string,$threshold) {
}
Or with new:
$slice = array($key=>$ruleSet[$key]);
$callbackClass = new ruleValidator();
foreach($slice as $rules => $rule)
/** ... */
call_user_func_array(array($callbackClass, $rls), $r)
Thanks #Deadooshka for providing me with the solution.
call_user_func_array("ruleValidator::$rls", $r)

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);

PHP Magic methods to use property as array or string

I have some property $abc (type array) in object:
$this->abc = array(
'id' => 123,
'status' => 'close'
);
There is some solution to using this property as array or string? Like that:
echo $this->abc; // return first element of array: 123
echo $this->abc['status']; // return element by key: close
Maybe getter and setter or Reflection?
EDIT:
I prepare some like this, but all returns id value:
class Test {
private $abc;
public function __construct() {
$this->abc = array(
'id' => '123',
'status' => 'close'
);
}
public function __get($key) {
if ($key === 'abc') {
echo $this->abc['id'];
}
}
}
$t = new Test();
echo $t->abc['id']; // return 123 - correct!
echo $t->abc['status']; // return 123 - incorrect, should be return 'close' string
echo $t->abc; // return 123 - correct
Any suggestion?
Actually you can't do exactly what your example shows.
Magic methods (what a silly name, btw) simply allow you to get and set properties, so that some_object->a can be handled with custom code, but you cannot tell PHP to handle
some_object->a and some_object->a[0] differently.
You are free to have your custom getter return an int, an array or an elephant, but that's it.
EDIT: Your code does nothing but print abc['id'] each time the property is referenced.
abc is still handled as any plain old property.
Let's replace echo with return
public function __get($key) {
if ($key === 'abc') {
return$this->abc['id'];
}
}
Now whenever you reference abc, the getter gives you "123".
Demonstration:
echo $t->abc['id']; // 'id' evaluates to 0, so result is "1" ("123"[0])
echo $t->abc['status']; // same thing
echo $t->abc; // "123" - correct (of sorts)
echo $t->abc[1]; // "2" (2nd character of "123")
You are also free do do stupid things like that:
class fairytale {
private static $handsome =
array ("Prince Valliant", "Superman", "Flash Gordon");
private static $ugly =
array ("Michael Moore", "Condoleezza Rice", "Ronald McDonald");
function __get ($prop)
{
if ($prop=='frog')
return self::$handsome;
if (preg_match ('/^frog\\[([0-9]*)\\]$/', $prop, $res))
return self::$ugly[$res[1]];
}
}
function kiss ($prince)
{
echo "$prince appears in a puff of smoke...\n";
}
$pond = new fairytale();
$frog1 = $pond->frog[0]; // <-- array subscript parsed before getter is called
$frog2 = 'frog[0]';
$frog2 = $pond->$frog2; // <-- array subscript parsed inside getter
kiss ($frog1);
kiss ($frog2); // <--- surprise!
As a side note, abusing custom getters/setters to turn frogs into princes might be a lot of fun and a fine display of PHP expertise, but it's unlikely to produce readable and maintainable code.
Just my opinion, of course.
If you want to process the value when fetch or set the value, you can use __get / __set
You can use magic methods __set and __get, for example
class someClass{
private $_arr = array();
public function __set($key, $val){
if(!array_key_exists($key, $this->_arr)){
$this->_arr[$key] = array();
}
$this->_arr[$key] = array_merge($this->_arr[$key], (array)$val);
}
public function &__get($key) {
return $this->_arr[$key];
}
}
$obj = new someClass();
$obj->setvalue = array(5,6,7);
$obj->setvalue = 4;
You could also just do
reset($this->abc);
for the first element.

PHP Object Validation

I'm currently working on an OO PHP application. I have a class called validation which I would like to use to check all of the data submitted is valid, however I obviously need somewhere to define the rules for each property to be checked. At the moment, I'm using arrays during the construction of a new object. eg:
$this->name = array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter
)
One array for each property.
I would then call a static method from the validation class which would carry out various checks depending on the values defined in each array.
Is there a more efficient way of doing this?
Any advice appreciated.
Thanks.
I know the associative array is used commonly to configure things in PHP (it's called magic container pattern and is considered bad practice, btw), but why don't you create multiple validator classes instead, each of which able to handle one rule? Something like this:
interface IValidator {
public function validate($value);
}
$validators[] = new StringLengthValidator(2, 10);
$validators[] = new NotNollValidator();
$validators[] = new UsernameDoesNotExistValidator();
This has multiple advantages over the implementation using arrays:
You can document them (very important), phpdoc cannot parse comments for array keys.
Your code becomes typo-safe (array('reqiured' => true))
It is fully OO and does not introduce new concepts
It is more readable (although much more verbose)
The implementation of each constraint can be found intuitively (it's not in a 400-line function, but in the proper class)
EDIT: Here is a link to an answer I gave to a different question, but that is mostly applicable to this one as well.
Since using OO it would be cleaner if you used classes for validating properties. E.g.
class StringProperty
{
public $maxLength;
public $minlength;
public $required;
public $value;
function __construct($value,$maxLength,$minLength,$required)
{
$this->value = $value;
$this-> maxLength = $maxLength;
$this-> minLength = $minLength;
$this-> required = $required;
}
function isValidat()
{
// Check if it is valid
}
function getValidationErrorMessage()
{
}
}
$this->name = new StringProperty($namefromparameter,10,2,true);
if(!$this->name->isValid())
{
$validationMessage = $this->name-getValidationErrorMessage();
}
Using a class has the advantage of encapsulating logic inside of it that the array (basically a structure) does not have.
Maybe get inspired by Zend-Framework Validation.
So define a master:
class BaseValidator {
protected $msgs = array();
protected $params = array();
abstract function isValid($value);
public function __CONSTRUCT($_params) {
$this->params = $_params;
}
public function getMessages() {
// returns errors-messages
return $this->msgs;
}
}
And then build your custom validators:
class EmailValidator extends BaseValidator {
public function isValid($val=null) {
// if no value set use the params['value']
if ($val==null) {
$val = $this->params['value'];
}
// validate the value
if (strlen($val) < $this->params['maxlength']) {
$this->msgs[] = 'Length too short';
}
return count($this->msgs) > 0 ? false : true;
}
}
Finally your inital array could become something like:
$this->name = new EmailValidator(
array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter,
),
),
);
validation could then be done like this:
if ($this->name->isValid()) {
echo 'everything fine';
} else {
echo 'Error: '.implode('<br/>', $this->name->getMessages());
}

Categories