I am trying to set a validation message for a set_rule....is this possible?
I tried this
$this->form_validation->set_rules('payment_amount', 'Payment Amount', 'regex_match[/^\d{0,4}(\.\d{1,2})?$/]');
$this->form_validation->set_message('payment_amount', 'This is a test');
and my message did not change.
Any ideas?
You can set a custom validation error message by creating a function for the rule.
This code is untested.
public function payment_amount($str)
{
if (preg_match('[/^\d{0,4}(\.\d{1,2})?$/]', $str)
{
$this->form_validation->set_message('payment_amount', 'The %s field has an error');
return FALSE;
}
else
{
return TRUE;
}
}
Us CI's callback as follows:
$this->form_validation->set_rules('payment_amount', 'Payment Amount', 'callback_payment_amount')
Create MY_Form_validation.php & put your code into.
Use the documentation (http://ellislab.com/codeigniter/user-guide/general/creating_libraries.html)
Extending Native Libraries
If all you need to do is add some functionality to an existing library - perhaps add a function or two - then it's overkill to replace the entire library with your version. In this case it's better to simply extend the class. Extending a class is nearly identical to replacing a class with a couple exceptions:
The class declaration must extend the parent class.
Your new class name and filename must be prefixed with MY_ (this item is configurable. See below.).
For example, to extend the native Email class you'll create a file named application/libraries/MY_Email.php, and declare your class with:
class MY_Email extends CI_Email {
}
Note: If you need to use a constructor in your class make sure you extend the parent constructor:
class MY_Email extends CI_Email {
public function __construct()
{
parent::__construct();
}
}
Related
This is a follow-up to my previous question about resolving the diamond issue in php.
As I state in that question, I resolve my problem by using traits and passing the instance of the class to the method of the trait. Such as:
trait SecurityTrait
{
public function beforeExecuteRouteTrait($controller, Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
return $this->beforeExecuteRouteTrait($this, $dispatcher);
}
}
However, I am still uncomfortable with this as I don't think this is how traits are really supposed to be used. In my reading I haven't found any way in which to access class members in traits (make $this inside a trait refer to the class using it). Is this possible? Or is there another way to implement a similar behaviour?
After reading some of the answers...
Previously I thought I had received errors when using $this->... inside the trait and this led me to believe the trait could not access anything to do with the underlying class. After reading the answers I tried altering my code to use $this->... inside a trait again and it works - which means a typo several weeks ago has given me far too much headache...
The example given previously now looks like this
trait SecurityTrait
{
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
}
Much cleaner and more easily understandable but provides the same functionality.
If you use a trait inside a class then that trait has full access to all class's members and vice versa - you can call private trait methods from the class itself.
Think of traits as code that literally gets copy/pasted into the class body.
For example:
trait Helper
{
public function getName()
{
return $this->name;
}
private function getClassName()
{
return get_class($this);
}
}
class Example
{
use Helper;
private $name = 'example';
public function callPrivateMethod()
{
// call a private method on a trait
return $this->getClassName();
}
}
$e = new Example();
print $e->getName(); // results in "example"
print $e->callPrivateMethod(); // results in "Example"
In my view referencing classes in traits is not the best way to use them but there's nothing stopping anyone from doing it.
No, that's exactly what Traits are for. Your class already extends a class so you can't inherit the methods and variables of any other classes.
Think of a Trait like copy/paste for code execution. When a class includes a Trait, it's just as if you had written all that code into the class itself.
Can someone explain this code in Silverstripe:
public function init() {
RSSFeed::linkToFeed($this->Link() . "rss");
parent::init();
}
What exactly is init function?
what parent::init();
exactly do in code
in php classes when you overwrite a method of parent class you still can call the parent class with this code, it will help you to put some code at the beginning of the real method without removing it.
you can find out more about it at php documentation
The upmost init() method is defined in the Controller class.
Then ContentController extends Controller, which overrides the Controller class's init() method, but it's also calling parent::init() on the first line. Then usually you define all your page controller classes like this (for any new page type), in the example below for the default Page_Controller class:
class Page_Controller extends ContentController {
public function init() {
parent::init();
// do your own stuff here
}
}
So this is the traditional PHP based class extension mechanism, but Silverstripe also allows you to use Extensions and Data Extensions, which is basically extending the functionality of already existing controllers, data objects. I won't go into details with this... You can find out more about this here: https://docs.silverstripe.org/en/4/developer_guides/extending/extensions/
I usually have something like this in my controller classes:
class Page_Controller extends ContentController {
public function init() {
parent::init();
// do your own stuff here
$this->extend('updateInit');
}
}
Notice the $this->extend('updateInit'); line above.
I can have another extension defined for the Page_Controller class inside a YAML config file somewhere, and than have the updateInit() method defined in that class. Example:
class Page_Controller_Extension extends Extension {
public function updateInit() {
// do some more stuff here
}
}
...and in this case you would have something like this in a YAML config file:
---
Name: siteextensions
After:
- 'framework/*'
- 'cms/*'
---
Page_Controller:
extensions:
- Page_Controller_Extension
Note that this is not really the traditional way of extending classes, like in PHP, it's more like defining some decorators for a controller class. Also, to refer to the parent, or object being decorated, you can't use just $this, you'll need to use $this->owner. Example below:
class Page_Controller_Extension extends Extension {
public function updateInit() {
// do some more stuff here
if ($this->owner->IsFeatured) {
// do something here
}
}
}
You usually decorate controllers extending the Extension class, and you extend the DataExtension class if you want to decorate DataObjects - works the same way as explained above.
Can anyone tell me if it's possible to override a use statement?
My example is having an MVC setup where there is core code with the ability to override each Controller / Model with a custom version that extends the core version.
The issue I face is that my core controller has a use statement telling it to use the core model, so if I extend the model, I'm not sure how to tell it to use the custom model rather than the core one
I could obviously update the core controller use statement to point to the custom one, but the core code is shared so the custom version may not exist on other sites that use this core core
Use statements are obviously file level so I'm guessing it's not possible, but I'm hoping there's either something I don't know about or maybe a workaround
Example
Core controller
namespace Core;
use Core\Model\Example as ExampleModel;
class ExampleController {
public function output() {
$model = new ExampleModel;
$model->test();
}
}
Core Model
namespace Core;
class ExampleModel() {
public function test() {
echo 'This is the core test';
}
}
Custom Controller
namespace Custom;
use Custom\Controller\Example as Base,
Custom\Model\Example as ExampleModel;
class ExampleController extends Base {
//Inherits the output() method
}
Custom Model
namespace Custom;
use Core\Model\Example as Base;
class ExampleModel extends Base {
public function test() {
echo 'This is the custom test';
}
}
So given this example, is it possible for me to create an instance of the custom controller which uses the custom model to output 'This is the custom test', without modifying the core code at all?
Hopefully what I'm asking makes sense
Thanks
I'm not quite sure I understand your question, but the answer should be self-evident: If your custom model extends from the core model, you can simply extend another class from that custom class
If you are writing code, that depends on a child of the core class being present, then that child class becomes a vital part of your project. If you can't change the core itself, add that class as a dependency. It's as simple as that.
Adding a second layer of inheritance needn't worry you, it's perfectly common to do so. Something like this is perfectly predictable, and reliable:
namespace Core;
class Model
{
public function coreTest()
{
return 'from the core';
}
}
namespace Custom;
use Core\Model;
class CustomModel extends Model
{
public function customTest()
{
return 'from the custom model';
}
}
//finally
namespace Project;
use Custom\CustomModel;
class ProjectModel extends CustomModel
{
public function test()
{
return array(
$this->coreTest(),
$this->customTest(),
'From the project'
);
}
}
$test = new ProjectModel();
echo implode(PHP_EOL, $test->test());
If, however you want a given class to extend from another class, based on whether or not that class exists, you are looking for conditional imports.
A simple use statement is evaluated at compile-time, so there's no way you can use an if check to switch between which class you extend from.
There is, however a hacky work-around, but I wouldn't rely on it. Check if the given class exists (without autoloading), and set an alias to the class that does.
if (!class_exists('\\Custom\\Model', false))
class_alias('\\Core\\Model', 'Base');
else
class_alias('\\Custom\\Model', 'Base');
class CustomModel extends Base
{}
But really: don't go down this route. Sure your code will work, but if you then rely on a method being available, that was defined in the custom class, but that class was missing, then your code will fail... horribly.
Details on conditional imports:
Why use class alisases?
I have an exception in application/core named prefix_Exceptions.php with the same class name. I try to throw this exception from a controller and I get:
Fatal error: Class 'prefix_Exceptions' not found in user_controller.php
In application/core/prefix_Exceptions.php:
<?php
class prefix_Exceptions extends CI_Exceptions {
public function __construct() {
parent::__construct();
}
public function test() {
echo "This is a test.";
}
}
And in application/controllers/user_controller.php:
<?php
class User_Controller extends CI_Controller {
public function view($id = '0') {
$this->load->model('user_model');
$u = $this->user_model->getUser($id);
if (!isset($u)) {
$this->exceptions->test(); // ???
}
echo "Test: $u";
}
}
Oh, and my prefix is set to prefix_:
$config['subclass_prefix'] = 'prefix_';
I've read about a dozen threads on this issue and none of them fix my exception so that it can be thrown by the controller.
The main reason your code is not working, is (as the error message suggests): your prefix_invalid_user.php is never loaded. CI does not know to load this file, as you are not following the required file naming scheme.
If you want to extend a built-in class, you have to use the same class name, except you change the prefix from CI_ to MY_ (or whatever prefix you set in your config).
To extend the class CI_Exceptions you would have to name it MY_Exceptions and save that php file in /application/core/MY_Exceptions.php. Then, and only then, will CI auto-load it for you.
However you should also know that CI's exceptions class isn't actually for throwing exceptions (the name is misleading, but CI_Exceptions handles error reporting). As you can see in the /system/core/Exceptions.php file, the CI_Exceptions class does not extend PHP's native Exceptions class, which is necessary to create custom, throwable exceptions.
If you want custom, throwable exceptions you have to create your own wrapper for them, and load/autoload it as a library.
Edit:
As per the OP's request, I'm adding the other half of the solution, which was to simply fetch the class object from CI's innards. For this, we can use the load_class function, which will return our class object if it has been instantiated, and if not, it will instantiate and return it.
$foo = load_class('Exceptions', 'core', $this->config->item('subclass_prefix'))
Then we can access the methods of our custom Exceptions class as so:
$foo->someMethodName();
I am creating a plugin for a CMS that provides a few base classes (let's say one of these classes is called Base). This class has a few helper methods that must be overwritten in the extending class. We should note that the base methods have default parameters/values provided. In one version of the LMS these values are provided by reference in the next version just by value.
For example (CMS v1.0):
function prepareTable(&$table){...
CMS v1.1:
function prepareTable($table){...
When you extend the Base class and overwrite the prepareTable method you have to declare it with the same default parameters/values as well, otherwise a STRICT PHP warning is displayed (on by default in PHP 5.4).
My question is, how do I conditionally overwrite the method from the parent class in a working way, knowing the version of the parent CMS?
Here's what I have currently (not working at the moment):
class Base{
function prepareTable(&$table){
}
}
class Extending extends Base{
if(CMS_VERSION=='1.0')
function prepareTable(&$table){
else
function prepareTable($table){
echo $table;
}
}
Obviously, I can not edit the Base and its method directly.
EDIT: Here's the exact error message:
Strict standards: Declaration of Extending::prepareTable() should be compatible with Base::prepareTable($table) in.
the only way I can think of achieving this without duplicating the code inside prepareTable is to create a pseudo function that gets called inside prepareTable and then declare that in the final extended class
if(CMS_VERSION=='1.0') {
class Base2 extends Base{
function prepareTable(&$table){
return $this->prepareTable2($table);
}
function prepareTable2(&$table){
}
}
} else {
class Base2 extends Base{
function prepareTable($table){
return $this->prepareTable2($table);
}
function prepareTable2(&$table){
}
}
}
class Extending extends Base2{
function prepareTable2(&$table){
echo $table;
}
}
if(CMS_VERSION=='1.0') {
class Extending extends Base{
function prepareTable(&$table){
}
}
} else {
class Extending extends Base{
function prepareTable($table){
}
}
}
Note that the if/else check must be done before the class is defined, not inside the class. Essentially, you are building two different versions of the class.
Side Note: If you need to include shared methods, that won't be changed between the two versions of the class, you can define a new class that will extend Extending, create the shared methods there and use this new class.
For example (place this after the code above):
class ExtendingFull extends Extending{
// Here you may include your shared methods
// e.g:
public function sharedMethod(){
echo 'test';
}
}