Creating own PHP framework, How to sending parameters to page controller - php

I'm creating my own framework. It works like this
localhost/controller/action/firstVariable/second/third (And so on...)
My bootstrap look like this:
$request = Util::getInput('request');
$requestList = explode("/",$request);
$modelName = #ucwords($requestList[0]);
$action = #$requestList[1];
$parameters = array_slice($requestList,2);
$controllerName = $modelName.'s'.'Controller';
I'm getting parameters from an url and save them in a variable $parameters. I would like to send them to the current action in my controller the way Laravel 5 is doing.
Example, in Laravel I specify parameters in the url and thats it.
To call them, I need to do a simple step. Just define them:
public function firstAction($first,$second){
}
When I go to an url like:
localhost/Main/firstAction/first/second/
Function of action 'firstAction' will catch those 2 parameters and then basically I can call them inside of the controller and send it to view.
My extends Controller class:
class Controller{
public function __construct($model,$action){
$modelClass = new main();
$reflection = new ReflectionClass($model.'sController');
$reflection->hasMethod($action) ? $this->$action() : die ('Base Controller call error: Method '. $action .' does not exist in Controller '. $model.'sController');
}
public static function renderView($action,$model,$data){
$model = str_replace('sController','',$model);
//include '../application/views/'.$model.'/'.$action.'.php';
$loader = new Twig_Loader_Filesystem('../application/views/'.$model);
$twig = new Twig_Environment($loader);
echo $twig->render($action.'.php', $data);
}
}
class MainsController extends Controller {
private $_data = array();
public function __construct($model,$action){
parent::__construct($model,$action);
}
public function firstAction($first,$second){
echo 'Hoi';
}
}
How can I do it, the good way? I can of course send the variable $parameter to MainController and than call
$this->_data inside of my action but It is not efficient.
I think I need to use arrays to do it, but I have no idea how.
Thank you.

Check out http://php.net/manual/en/function.call-user-func-array.php
P.S.
You do not have to use reflection in order to check if method on that object's instance exist. Single function call can be enough. Check out http://php.net/manual/en/function.is-callable.php
It would be nice if you would use more descriptive names. Now they are confusing.

Related

Is it possible to invoke an instance of a class whose name is passed as a variable?

I'm developing in Laravel 9, though I assume this is Php-specific. Example below of what I'm trying to achieve: Imagine I have a controller named HomeController.php with a getData() method that returns something I need...
<?php
namespace App\Http\Controllers;
class HomeController
{
public function getData()
{
return [my data]
}
}
And I want to be able to call that class and method in a dynamic way, and assign my data to $data...
<?php
use App\Http\Controllers\HomeController;
class Example
{
public $className = 'HomeController';
public $method = 'getData';
public function index()
{
$instance = new $this->className;
$method = $this->method;
$data = $instance->$method();
}
}
I have a variation of this setup in my application, and it's not working. I get the following error: Class "HomeController" not found.
If I replace $this->className with HomeController it works. Keep in mind $className will be passed from elsewhere, I want to avoid hard-coding class names into my Example class.
It is true that I will still need to include them all at the top anyway, but I just want to know if it's possible to pass a class name like that. Unless there's a way to dynamically include those too, but I doubt it.
Edit: Tim's answer in the comments worked great. Here is a fixed version:
<?php
use App\Http\Controllers\HomeController;
class Example
{
public $className = 'App\\Http\\Controllers\\HomeController'; // Change 1
public $method = 'getData';
public function index()
{
$instance = app()->make($this->className); // Change 2
$method = $this->method;
$data = $instance->$method();
}
}

Should a custom find method on a Laravel Model be static?

In the following Laravel 5 Model should the findByIdAndCourseOrFail method be static?
class Section extends Model {
//should this method be static?
public function findByIdAndCourseOrFail($id, $courseId)
{
$result = $this->where('id', $id)->where('course_id', $courseId)->first();
if (!is_null($result))
{
return $result;
}
throw (new ModelNotFoundException())->setModel(Section::class);
}
}
With the controller:
class SectionsController extends Controller {
protected $sections;
public function __construct(Section $section)
{
$this->sections = $section;
}
public function foo($id, $courseId) //illustration only
{
$section = $this->sections->findOrFail($id);
$section = $this->sections->findByIdAndCourseOrFail($id, $courseId);
//would need to be non-static
$section = Section::findByIdAndCourseOrFail($id, $courseId);
//weird when compared with find above
}
On the one hand, we're not acting on a Section instance [See Note]. On the other hand, in a controller with auto-dependency injection through Laravel's service container we'd act on an instance: $sections = $this->sections-> findByIdAndCourseOrFail(7,3); and my IDE (PhpStorm) squawks if Static.
[Note]: This comment may be a misunderstanding of how Laravel Models work. For me, I would expect that find(), findOrFail() to be Class methods and thus Static as opposed to the instance that a find method would return.
I'm not sure if local scopes are meant to be used like that. But it works for me on laravel 5.2:
public function scopeFindByIdAndCourseOrFail($query, $id, $courseId)
{
$result = $query->where('id', $id)->where('course_id', $courseId)->first();
if (!is_null($result))
{
return $result;
}
throw (new ModelNotFoundException())->setModel(Section::class);
}
In the controller you can use it both ways:
$section = Section::findByIdAndCourseOrFail($id, $courseId);
Or
$model = new Section();
$section = $model->findByIdAndCourseOrFail($id, $courseId);
class Section extends Model {
public static function findByIdAndCourseOrFail($id, $courseId)
{
$result = self::where('id', $id)->where('course_id', $courseId)->first();
if (!is_null($result))
{
return $result;
}
throw (new ModelNotFoundException())->setModel(Section::class);
}
}
Personally I would make this a static method, I'm not sure if there is a "correct" answer though as either can be done. The way I kind of separate them in my mind is if I'm doing something to an instance of a model then I make it a normal public function. If I am doing something to the Collection I use a static. For example:
$person = new Person();
$person->setAdmin(true);
$person->save();
// OR
$admins = Person::getAdmins();
In the first example we have a specific instance of a Person and we are manipulating it, all code would be simply manipulating that specific instance. In the second example we are acting on the entire collection of Person and we want a collection of objects to be returned.
In your case you would have to initiate an instance of Section just to be able to use your non-static public method, like this:
$section = new Section();
$foundSection = $section->findByIdAndCourseOrFail(7,3);
So $section becomes a temporary variable that is never really used. On the other hand if you made it a static you could call it without having to do this.
$section = Section::findByIdAndCourseOrFail(7,3);
Hopefully that makes sense.

calls to model in Codeigniter Constructor... how to

I am developing a CRM for our agency in Codeigniter and I have a question that I can't seem to find a solid answer on. If I have a task that I do on the majority of methods in a controller, is there any way to define that action only once? For instance...
Every view call gets passed the $data variable, like so...
$this->load->view('templates/template.php', $data);
So if I am doing something like getting the admins information in every function of the controller, how can i tell it to do that action ONE time and pass it to all my functions.
Like this...
$data['admin'] = $this->Crm_model->get_admin();
I've tried putting that ^ in the constructor and it doesn't work. Any ideas?
If you do:
$data['admin'] = $this->Crm_model->get_admin();
in the constructor, $data's scope is limited to the constructor. You need to create it as a class property so it is scoped to the entire class. Do this instead
$this->data['admin'] = $this->Crm_model->get_admin();
in the constructor, and then in other methods, you can access the array by doing $this->data
Here's an example:
class Foobar extends CI_Controller {
public function __construct() {
$this->data['foo'] = "bar";
}
public function index() {
// use the class property data here to add more info to it
$this->data['hello'] = "world";
// now pass this to the view
$this->load->view('myView', $this->data);
// myView will receive both $foo and $hello
}
}

Dynamic method call in OOP

I don't have alot of experience with OOP programming in PHP, and my search has given no result but solutions to direct methods. What I need is this:
// URL Decides which controller method to load
$page = $_GET['page'];
// I want to load the correct controller method here
$this->$page();
// A method
public function home(){}
// Another method
public function about(){}
// e.g. ?page=home would call the home() method
EDIT: I've tried several of the suggestions, but what I get is a memory overload error message. Here is my full code:
<?php
class Controller {
// Defines variables
public $load;
public $model;
public function __construct() {
// Instantiates necessary classes
$this->load = new Load();
$this->model = new Model();
if (isset($_GET['page'])) {
$page = $_GET['page'];
$fc = new FrontController; // This is what crashes apparently, tried with and without ();
}
}
}
If I understand your question correctly, you'd probably want something more like this:
class FrontController {
public function home(){ /* ... */ }
public function about(){ /* ... */ }
}
$page = $_GET['page'];
$fc = new FrontController;
if( method_exists( $fc, $page ) ) {
$fc->$page();
} else {
/* method doesn't exist, handle your error */
}
Is this what you're looking for? The page will look at the incoming $_GET['page'] variable, and check to see whether your FrontController class has a method named $_GET['page']. If so, it will be called; otherwise, you'll need to do something else about the error.
You can call dynamic properties and methods using something like this:
$this->{$page}();
Use a class.
Class URLMethods {
public function home(){ ... }
public function about(){ ... }
}
$requestedPage = $_GET['page'];
$foo = new URLMethods();
$foo->$requestedPage();
You can achieve this by using call_user_func. See also How do I dynamically invoke a class method in PHP?
I think you'd like also to append another string to the callable functions like this:
public function homeAction(){}
in order to prevent a hacker to call methods that you probably don't want to be.

Prefill a form's widget with URL parameter (symfony)

All is in the title.
I get the URL param :
$log = $request->getParameter('logement');
Widget's statement :
$this->widgetSchema['logement'] = new sfWidgetFormInputText();
And I pass it in the form to prefill my widget 'logement' :
$this->form = new bailForm(array('logement' => $log));
I have read it in symfony's doc, but, when I do this, I have this error :
The "BailForm" form only accepts a "Bail" object.
I have already tried many things found on Internet but, no one works.
EDIT
The ORM is Doctrine
"Logement" is an attribute of "Bail"
EDIT 2
I have tried :
$log = $request->getParameter('logement');
$this->form = new bailForm(null, array('logement' => $log));
I don't have error, but my widget "logement" isn't filled...
One of two ways:
1. If you want to validate Logement
$form = new BailForm(); //BailForm must have Logement validator set
$form->bind(array('logement' => $log) + $otherRequestParameters);
$form->updateObject(); //or save
2. If you just want Logement set on the object
$bail = new Bail();
$bail->Logement = $log;
$form = new BailForm($bail);
Your form is a propel or doctrine form, the first parameter of the constructor has to be a linked object instance. Try this:
$this->form = new bailForm(null, array('logement' => $log));
The forms that are auto-generated based on model classes (in this case, BailForm for Bail), are of type sfFormObject, and thus accept only parameters of type corresponding to the model class.
A naive solution is to declare a custom constructor for type BailForm that takes an array as a single parameter (or an array and an object of type Bail).
This would not be very good practice however, as model forms are designed to work with model classes only. This logement parameter - what is its significance with respect to the Bail object? Maybe if you ask yourself that question, you can come up with a more suitable design that probably incorporates the logement as an attribute of Bail.
class QuestionsForm extends BaseForm
{
private static $email;
public static function setEmail($set) { self::$email = $set; }
public static function getEmail() { return self::$email; }
public function configure()
{
$this->setDefault('email', self::$email);
//$this->setDefault('email', 'testemail');
//rest of the form setup code
}
}
Here is the actions class
class questionsActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->email = $this->getRequestParameter('email');
QuestionsForm::setEmail($this->email);
//die(QuestionsForm::getEmail());
$f = new QuestionsForm();
$this->form = $f;

Categories