How can I make my validation class extendible? - php

I am writing a validation class in PHP which I would like to be extendible without editing the main parent class. I have provided a simplified version below of what I hope to achieve. I am passing a function/method name to validate() which first checks if it exists and if it does invokes it to check the variable I passed is valid. I am new to OOP and having problems with scope / visibility as I'm unable to get any custom validation rules in the child class working without hardcoding the name of the child class in the parent class. What is the best way to go about this? Many thanks for any assistance you can provide.
$rule = "number";
$var = "abcdef";
class Validation
{
public static function validate($rule, $var) {
if (is_callable("self::{$rule}")) {
return self::$rule($var);
}
}
protected static function number($var) {
return (preg_match("/^[0-9]+$/i", $var));
}
}
class MyRules extends Validation
{
public static function letter($var) {
return (preg_match("/^[a-zA-Z]+$/i", $var));
}
}
print MyRules::validate($rule, $var) ? "Valid!" : "Not valid!"; // Not valid!

Firstly, you can prevent overriding of the validate method using the final keyword:
public static final function validate($rule, $var) {
As for not being able to call static methods of subclasses, this can be done using Late Static Binding:
class Validation {
public static final function validate($rule, $var) {
if (is_callable("static::$rule")) {
return static::$rule($var);
}
}
}

This sounds like a job for strategy design pattern. It can be used if you want different variants of an algorithm, in your case the validation.

Here is how i work with my self made framework / libraries :
Like most n-tier systems , (MVC is like that) , the data must be checked b4 it is passed to the back-end tier(the database) ... so in a mvc arhitecture u can make a model that is the corespoindent to a db table , put in there the queries connection and validation... That model class needs to know only about that table data and nothing more...
If ull get to a point and see that ur validations get to some kind of routine... u can make a library... Or u can think from start what kind of data will ur app have...
P.S. : so for each data type in a table write a the correspondent validation, if the validations repeat make a library and call the lib validation method for the coresponding db data type

Related

Accept two different model instances as a function argument php

I'm writing a helper function to check whether any address information is present in my database by checking if any of the relevant fields don't equal null.
So, I have two models which I need to accept in the function as an argument.
App\Customer
App\Supplier
The variable passed can be either one.
For a function that should only accept one kind of model, I can just do this:
function check_for_address_info(App\Customer $customer) {
// other unrelated stuff
}
Is there a way to accept both models, or do I have to manually check it in the function by doing something like this:
function check_for_address_info($param) {
if(!is_a($param, 'App\Customer' || !is_a($param, 'App\Supplier')) {
// not an instance of either model
return false;
}
// do stuff as normal
}
Any ideas on how to accept two different models as a function argument?
I'm on Laravel 5.8.
There is two approaches, if it makes sense inheritance wise, you can extend a parent model and declare it as the type of the parameter. This will be checked on run time and provide an error if you pass the wrong type to the method.
public class Profile extends Model {
}
public class Customer extends Profile {
}
public class Supplier extends Profile {
}
function check_for_address_info(App\Profile $customerOrSupplier) {
}
In weak typed languages, it is common to have parameters that are generic. The way PHP solve this problem, is you can declare it in PHP Doc blocks. This will not check types on run time and is mostly for typehinting, documentation and static analysis tools.
/**
* #parameter \App\Customer|\App\Supplier $customerOrSupplier
**/
function check_for_address_info($customerOrSupplier) {
}

PHP Design pattern to pass a class name and check it for the class existence

I have several classes handling data validation and API requests preparation for every external API function. An example of those:
class PostProductFunction($user_params)
{
validate()
{
//...
}
invoke($user_params)
{
$this->validate();
//doing request...
}
}
I have an APIAccount class to represent one of several API accounts. It handles auth and it has a method
function invokeFunction($functionClassName, $user_params)
{
// check if the class $functionClassName exists and creates an instance of it
// ... $obj = new $functionClassName();
$obj->invoke($user_params);
}
So, the function class doesn't know about auth stuff and the APIAccount class doesn't know about user data structure.
The question is how to handle this $functionClassName inside the APIAccount class. Do I need to store all names of function classes somewhere? Do I need some kind of enum class? I don't want to simply take a string and then check whether the class with this name exists because the programmer passing this string can easily mistype, and in general he needs documentation to know the proper function name. I want that he somehow see all available options with something like enum. Do you have any ideas how to better implement it?
I'm not sure you need a design pattern for this. The following should satisfy your needs:
function invokeFunction($functionClassName, $user_params)
{
if (! class_exists($functionClassName)) {
throw new Exception("Class {$functionClassName} does not exist.");
}
$obj = new {$functionClassName}();
$obj->invoke($user_params);
}
I figured out how to both keep the invocation method signature and make it easier for programmers to see the hint with possible class names to pass in IDE.
The method itself remains as it is:
function invokeFunction($functionClassName, $user_params)
{
// check if the class $functionClassName exists and creates an instance of it
if (! class_exists($functionClassName)) {
throw new Exception("Class {$functionClassName} does not exist.");
}
$obj = new $functionClassName();
$obj->invoke($user_params);
}
However, in the classes which names can be passed as $functionClassName one needs to add a static method like this:
public static function getFunctionName()
{
$function_name = get_called_class();
// .... some optional operations with the name depending on what part of
// (or the full name) invokeFunction is anticipating
return $function_name
}
Then calling the invokeFunction is as simple as:
invokeFunction (SomeCertainFunctionClass::getFunctionName(), array('some_param'=>'some_val'));
Using this approach you can have your IDE hint when typing 'SomeCertainFunctionClass' and at the same time you can use a class name string as a function argument without any downfalls.

how to do something before setting a public attribute of the object?

If I get a class looks like this :
class a {
public $aa;
public $ab;
}
$t = new a();
now,
If I type $t->, I would got a tip list under zend Studio, but now I need to do something like this:
class a {
public $aa;
public $ab;
public function __get( $name ){
$this->$name = x;
return $this->$name;
}
}
so, what should I do? First I still want to get that tip list, and only public attribute could be listed. and idea about this?
It seems to me, that you're having two interlocking problems here.
If you add public fields to your class, then you can see them in autocomplete suggestions and it is easier for you to write code, but if you do that, then __get is not executed because this is a visible property and __get is only called for the invisible properties.
If you remove the public fields (or make them private so as to make them invisible), then your __get is executed but you don't get any autocomplete suggestions as there are no visible fields. This way your code works, but it is harder for you to write the code as you get no autocomplete suggestions.
So my suggestion to you would be to forget the __get functionality altogether. Pretend that it does not exist (since it does not do what you want it to do anyway). Use member functions instead of public properties and you'll get the autocomplete suggestions as well as your code executed. Yes you'll have to implement more methods that way, but that's life, you can't always have everything you want, you have to compromise.
What you can do is use something from the Java book and define methods for getters and setters (so either function getName() and function setName($value) or if you prefer you can use function name($value = null) and either set or get depending on the passed in value) or you can assign those values in a constructor if you don't care about the laziness part and the only thing you'll do is assign values.

Class methods and instance methods in codeigniter, how can I use them?

I am very new to codeigniter but understand OOP and MVC as I do a lot of Rails development. One thing I haven't figured out yet is how to write a class level method in codeigniter and access it in a controller. For example, I have
<?php
class User_model extends Model {
function user_model()
{
parent::Model();
}
public static function get_total_users_count(){
$results = $this->db->query("SELECT * FROM bhr_users GROUP BY userid");
if($results){
return $results->num_rows();
}
return 0;
}
}
?>
I think what I have done here is established a class level method for my model that I should be able to call with User_model::get_total_users_count() Now in my controller which a previous programmer called "Welcome" I have something like:
<?php
class Welcome extends Controller {
function Welcome()
{
parent::Controller();
$this->load->model('bhr_model');
$this->load->model('user_model');
}
function index()
{
$invite = $this->uri->segment(3);
if($invite == 'invitefriends') {
$pagedata['invitefriends'] = $invite;
} else {
$pagedata['invitefriends'] = '';
}
$pagedata['numberofpeople'] = User_model::get_total_users_count();
$this->load->view('default_page', $pagedata);
}
}
The above method call to get_total_users_count does not work because it says because I am using the db method on a class level function in get_total_users_count. In other words $this has no db method when I reference a class.
So now my question is a bit more theoretical. I always thought that instance methods should only be used when a method is acting on a specific instance of an class. Makes sense, right? However, get_total_users_count is acting on all "users" and counting them. It just seems like that should be a class level method. Do you agree? If do, do you know how I can access the database from withing the framework inside a class level function?
Thanks!
Since you are not instantiating User_model, you must get the CI instance, then use that for your db queries.
Inside get_total_users_count():
$ci_ins =& get_instance();
$ci_ins->db->query();
You can make your class as a helper so it will not be load as a instance. Only the code will be included so you can just call it as:
$sample = class_name::method();
CodeIgnighter works is by instantiating your models as you load them. What Thorpe Obazee said is the correct codeIgnighter way to use your Model.
What you are asking is if you can use a static method as you'd expect in most circumstances, which just isn't how CI works.
To accomplish what you're after, mives points out get_instance() which is the correct way to get at the main CI object. I use that way myself to do what you're doing.
get_total_user_count is more of a function for a user table gateway.
User model should have things like getUsername and getLastLogin.
User Table Gateway should have things like findUserById, createNewUser, and getTotalUserCount
$pagedata['numberofpeople'] = $this->user_model->get_total_users_count();
That's the CI way.

PHP: Where should validation functions be stored?

I'm using a PHP MVC Framework, and I've been just lumping all my validation functions in a helper class which get called when I need them.
However, isn't the Model for logic about working with data, not exclusively databases? Would it make more sense to put the validation functions in one of my models?
Thanks
The Model layer is for modelling how your application would work in the real world, so it's not just necessarily for dealing with data.
A common approach to validation functions is to create a Validator class for each type of validation you wish to perform. The Validators should implement an interface, so that any code utilizing a Validator object can be assured that they all work the same.
Something like this:
interface iValidator
{
public function validate($mixed);
public function getMessage();
}
class Validator_Email implements iValidator
{
public function validate($data)
{
//validate an email address
}
public function getMessage()
{
return 'invalid email address.';
}
}
class Validator_PositiveInteger implements iValidator
{
public function validate($data)
{
return ctype_digit((string) $data) && $data != 0;
}
public function getMessage()
{
return 'must be a positive integer';
}
}
If you're using a framework, it may already have validation classes that you can utilize or extend in a similar manner.
Yes, I would put validation in the models. However, you didn't mention which MVC framework you're using. If you're using CodeIgniter and its built in validation, it kind of forces you to validate in the controller.
Validation in this case is coming from a user input - you're going to want to validate the same input 'type' for many models, potentially. I'd go with a set of Validation classes (see Zend_Validate) and use them as input filters/validators in your Controller.
Since its In/Out manipulation, I see Controller as the place for that. Model should be more concerned about making sure your data is stored and retrieved.

Categories