Imagine I have a URL that loads a controller if the name matches (Ignoring any security issues with this at the moment hehe)
public function Load( $controller, $action = "Index" )
{
require_once( "Controllers/" . $controller . "Controller.php" );
$controllerName = $controller . "Controller";
$loadedController = new $controllerName();
$actionName = "ActionResult_" . $action;
$loadedController->$actionName();
}
Now imagine I want a log in form to send its $_POST details as parameters of the receiving controller launched above:
<?php
class ExcelUploadController extends Controller
{
public function ActionResult_Login( $username = NULL, $password = NULL )
{
// The parameters need to be mapped to the $_POST parameters names probably from the Load method somewhere and pumped in to the $loadedController->$actionName();
$post_username = $username;
$post_password = $password;
$this->ReturnView( "ExcelUpload/Index" );
}
}
?>
But also so that it does not matter what order the parameters are declared, it matches the parameter in the function based on the $_POST key.
How might I go about doing this, any ideas?
So to clarify if this doesn't make sense.. the method might look something like this:
public function Load( $controller, $action = "Index" )
{
require_once( "Controllers/" . $controller . "Controller.php" );
$controllerName = $controller . "Controller";
$loadedController = new $controllerName();
$actionName = "ActionResult_" . $action;
$checkIfPostData = $_POST;
if( isset( $checkIfPostData ) )
{
// Do some funky wang to map the following $loadedController->$actionName();
// with the username and password or any other $_POST keys so that in the calling method, I can grab hold of the $_POST values
}
$loadedController->$actionName();
}
What your are looking for is call_user_func_array()
EDIT, to reply to comment :
You have two options: rewrite all your function so that they accept only one array() as argument and you parse that array for values. A bit fastidious but it can be useful in some cases. Or you can request for the required argument of a function:
// This will create an object that is the definition of your object
$f = new ReflectionMethod($instance_of_object, $method_name);
$args = array();
// Loop trough params
foreach ($f->getParameters() as $param) {
// Check if parameters is sent through POST and if it is optional or not
if (!isset($_POST[$param->name]) && !$param->isOptional()) {
throw new Exception("You did not provide a value for all parameters");
}
if (isset($_POST[$param->name])) {
$args[] = $_POST[$param->name];
}
if ($param->name == 'args') {
$args[] = $_POST;
}
}
$result = call_user_func_array(array($instance_of_object, $method_name), $args);
That way your array will be properly constructed.
You can also add some specific treatment whether a parameter is optional or not (I guess you can understand how to do it from the code I gave you ;)
Since the data is being sent through POST you don't need to pass any parameters to your method:
class ExcelUploadController extends Controller {
private $userName;
private $login;
public function ActionResult_Login() {
$this->userName = $_POST['username'];
$this->login = $_POST['login'];
}
}
Don't forget to sanitize and validate the user input!
Related
I have a function where I save a group. I want to "access" it from the page (when the user makes a new group with a form) and from a controller, too (when a process makes a new group, for example when I create a new tenant)
My code is so far:
.
.
.
$this->saveGroup($tenantId, 'Default group');
.
.
.
public function saveGroup(Request $request, $tenantId = 0, $nameFromFunction = ''){
if(!empty($request)){
$name = $request -> name;
} elseif($nameFromFunction != ''){
$name = $nameFromFunction;
} else {
$name = '';
}
if($tenantId > 0 && $name != ''){
$group = new ConversationGroup;
$group -> group_name = $name;
$group -> tenant_id = $tenantId;
$group->save();
}
if($nameFromFunction != ''){
return $group -> id; //if a function calls it
} else {
return redirect("/{$tenantId}/groups"); //if the new group was made from the page
}
}
If I test it with the page's group creation form it works fine, but when I run it from the controller I got the following error:
GroupController::saveGroup() must be an instance of Illuminate\Http\Request, integer given
What I must change if I want to earn this logic?
you could use global request() helper and completely avoid passing Request object to your function, like:
public function saveGroup($tenantId = 0, $nameFromFunction = ''){
//get input named 'name' or default to $nameFromFunction
$name = request('name', $nameFromFunction);
...
The reason this is happening, is because in your function definition, first parameter is not $tenantId, but the object of class request.
So you have to pass an object of request class as a first parameter to get this working.
You should try this way,
public function otherFunction()
{
$this->saveGroup(new Request(['name' => 'Default Group']), $tenantID);
OR
$this->saveGroup(new Request(), $tenantID, "Default Group");
}
public function saveGroup(Request $request, $id = 0, $nameFromFunction = '')
{
echo 'here-your-code';
}
I was looking for a solution as to how to save data from joomla frontend. I came across the following code for controller and model which works perfectly. But I was looking for a standard practice like its done in the back end using jform, jtable etc ... In the following code (inside model), the saving technique do not look so appealing. And I am totally without any idea how the server side validations is implemented.
It might be confusing, so i would like to reiterate that in the backend we don't even have to write the add or save or update function, it is automatically handled by the core classes with both client and server side validation. So i was looking for something like that.
Controller
<?php
// No direct access.
defined('_JEXEC') or die;
// Include dependancy of the main controllerform class
jimport('joomla.application.component.controllerform');
class JobsControllerRegistration extends JControllerForm
{
public function getModel($name = 'Registration', $prefix = 'JobsModel', $config = array('ignore_request' => true))
{
return parent::getModel($name, $prefix, array('ignore_request' => false));
}
public function submit()
{
// Check for request forgeries.
JRequest::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
// Initialise variables.
$app = JFactory::getApplication();
$model = $this->getModel('Registration');
// Get the data from the form POST
$data = JRequest::getVar('jform', array(), 'post', 'array');
$form = $model->getForm();
if (!$form) {
JError::raiseError(500, $model->getError());
return false;
}
// Now update the loaded data to the database via a function in the model
$upditem = $model->updItem($data);
// check if ok and display appropriate message. This can also have a redirect if desired.
if ($upditem) {
echo "<h2>Joining with us is successfully saved.</h2>";
} else {
echo "<h2>Joining with us faild.</h2>";
}
return true;
}
}
Model
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// Include dependancy of the main model form
jimport('joomla.application.component.modelform');
// import Joomla modelitem library
jimport('joomla.application.component.modelitem');
// Include dependancy of the dispatcher
jimport('joomla.event.dispatcher');
/**
* HelloWorld Model
*/
class JobsModelRegistration extends JModelForm
{
/**
* #var object item
*/
protected $item;
/**
* Get the data for a new qualification
*/
public function getForm($data = array(), $loadData = true)
{
$app = JFactory::getApplication('site');
// Get the form.
$form = $this->loadForm('com_jobs.registration', 'registration', array('control' => 'jform', 'load_data' => true),true);
if (empty($form)) {
return false;
}
return $form;
}
//Nwely added method for saving data
public function updItem($data)
{
// set the variables from the passed data
$fname = $data['fname'];
$lname = $data['lname'];
$age = $data['age'];
$city = $data['city'];
$telephone = $data['telephone'];
$email = $data['email'];
$comments = $data['comments'];
// set the data into a query to update the record
$db = $this->getDbo();
$query = $db->getQuery(true);
$query->clear();
$db =& JFactory::getDBO();
$query = "INSERT INTO #__joinwithus ( `id`, `firstname`, `lastname`, `age`, `city`, `telephone`, `email`, `comment`)
VALUES (NULL,'" . $fname . "','" . $lname . "','" . $age . "','" . $city . "','" . $email . "','" . $telephone . "','" . $comments . "')";
$db->setQuery((string)$query);
if (!$db->query()) {
JError::raiseError(500, $db->getErrorMsg());
return false;
} else {
return true;
}
}
}
Can somebody kindly point me to a good tutorial or share me a component which deals with form in the frontend with joomla 2.5.
use the following code in your model
$data = $app->input->getArray($_POST);
$query = $db->getQuery(true);
You should be able to use jcontrollerform's methods directly, instead of writing your own submit()-method (and updItem()) like you do. I describe something similar here. This means you display your form the usual way using jform, and use action="index.php?option=com_jobs&task=save&view=registration&id=whateverid"
This way jcontrollerform->save() is used, which in turn calls your model's save(). (Hmmm, this probably means your model should extend JModelAdmin instead of JModelForm, to include the relevant methods. ) This will run all the necessary validation checks etc.
You might need to register paths for the model, table and form that you want to be using, like I do in the link.
You need to include the id in the url parameters if you edit existing data, because the jform[id] - parameter will be ignored.
Sorry I dont have any good tutorial or whatever for you, hope this helps.
I am getting get parameters using this
$this->params()->fromQuery('KEY');
I found two way to get POST parameters
//first way
$this->params()->fromPost('KEY', null);
//second way
$this->getRequest()->getPost();
Both of this working in "POST" method but now in a "PUT" method if I pass values as a post parameters.
How I can get post parameters in "PUT" method?
I guess the right way of doing that is by using Zend_Controller_Plugin_PutHandler:
// you can put this code in your projects bootstrap
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_PutHandler());
and then you can get your params via getParams()
foreach($this->getRequest()->getParams() as $key => $value) {
...
}
or simply
$this->getRequest()->getParam("myvar");
You need to read the request body and parse it, something like this:
$putParams = array();
parse_str($this->getRequest()->getContent(), $putParams);
This will parse all params into the $putParams-array, so you can access it like you would access the super globals $_POST or $_GET. For instance:
// Get the parameter named 'id'
$id = $putParams['id'];
// Loop over all params
foreach($putParams as $key => $value) {
echo 'Put-param ' . $key . ' = ' . $value . PHP_EOL;
}
I had trouble using PUT data sent from AngularJS and found the best way was to use a custom Zend plugin
class Web_Mvc_Plugin_JsonPutHandler extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
if (!$request instanceof Zend_Controller_Request_Http) {
return;
}
if ($this->_request->isPut()) {
$putParams = json_decode($this->_request->getRawBody());
$request->setParam('data', $putParams);
}
}
}
Which can then be accesses via getParams as a PHP object
$data = $this->getRequest()->getParam('data');
$id = $data->id;
There's a piece of code:
if(DataEndpoint::isAjaxRequest()) {
if(isset($_POST['controller']) && !empty($_POST['controller']) && isset($_POST['action']) && !empty($_POST['action'])) {
$controllerName = $_POST['controller'];
$actionName = $_POST['action'];
if(class_exists($controllerName.'Controller')) {
$controller = new $controllerName.'Controller';
if(method_exists($controller, $actionName)) {
// if id's been passed
// if method signature accepts the parameter
// invoke... ?
}
}
}
} // if(DataEndpoint::isAjaxRequest()) {
I can check whether given action exists but don't know how to pass/invoke the action with additional parameters like id (let's suggest it is a string and it is optional). How do I solve this?
use ReflactionClass and ReflactionMethod. look at here:
$action_ref = new ReflectionMethod($controller, $action);
$action_required_params = $action_ref->getParameters();
$parameters = array(/*...*/);
$action_ref->invokeArgs($controller, $parameters);
I have been reading a lot about how and why to use an MVC approach in an application. I have seen and understand examples of a Model, I have seen and understand examples of the View.... but I am STILL kind of fuzzy on the controller. I would really love to see a thorough enough example of a controller(s). (in PHP if possible, but any language will help)
Thank you.
PS: It would also be great if I could see an example of an index.php page, which decides which controller to use and how.
EDIT: I know what the job of the controller is, I just don't really understand how to accomplish this in OOP.
Request example
Put something like this in your index.php:
<?php
// Holds data like $baseUrl etc.
include 'config.php';
$requestUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$requestString = substr($requestUrl, strlen($baseUrl));
$urlParams = explode('/', $requestString);
// TODO: Consider security (see comments)
$controllerName = ucfirst(array_shift($urlParams)).'Controller';
$actionName = strtolower(array_shift($urlParams)).'Action';
// Here you should probably gather the rest as params
// Call the action
$controller = new $controllerName;
$controller->$actionName();
Really basic, but you get the idea... (I also didn't take care of loading the controller class, but I guess that can be done either via autoloading or you know how to do it.)
Simple controller example (controllers/login.php):
<?php
class LoginController
{
function loginAction()
{
$username = $this->request->get('username');
$password = $this->request->get('password');
$this->loadModel('users');
if ($this->users->validate($username, $password))
{
$userData = $this->users->fetch($username);
AuthStorage::save($username, $userData);
$this->redirect('secret_area');
}
else
{
$this->view->message = 'Invalid login';
$this->view->render('error');
}
}
function logoutAction()
{
if (AuthStorage::logged())
{
AuthStorage::remove();
$this->redirect('index');
}
else
{
$this->view->message = 'You are not logged in.';
$this->view->render('error');
}
}
}
As you see, the controller takes care of the "flow" of the application - the so-called application logic. It does not take care about data storage and presentation. It rather gathers all the necessary data (depending on the current request) and assigns it to the view...
Note that this would not work with any framework I know, but I'm sure you know what the functions are supposed to do.
Imagine three screens in a UI, a screen where a user enters some search criteria, a screen where a list of summaries of matching records is displayed and a screen where, once a record is selected it is displayed for editing. There will be some logic relating to the initial search on the lines of
if search criteria are matched by no records
redisplay criteria screen, with message saying "none found"
else if search criteria are matched by exactly one record
display edit screen with chosen record
else (we have lots of records)
display list screen with matching records
Where should that logic go? Not in the view or model surely? Hence this is the job of the controller. The controller would also be responsible for taking the criteria and invoking the Model method for the search.
<?php
class App {
protected static $router;
public static function getRouter() {
return self::$router;
}
public static function run($uri) {
self::$router = new Router($uri);
//get controller class
$controller_class = ucfirst(self::$router->getController()) . 'Controller';
//get method
$controller_method = strtolower((self::$router->getMethodPrefix() != "" ? self::$router->getMethodPrefix() . '_' : '') . self::$router->getAction());
if(method_exists($controller_class, $controller_method)){
$controller_obj = new $controller_class();
$view_path = $controller_obj->$controller_method();
$view_obj = new View($controller_obj->getData(), $view_path);
$content = $view_obj->render();
}else{
throw new Exception("Called method does not exists!");
}
//layout
$route_path = self::getRouter()->getRoute();
$layout = ROOT . '/views/layout/' . $route_path . '.phtml';
$layout_view_obj = new View(compact('content'), $layout);
echo $layout_view_obj->render();
}
public static function redirect($uri){
print("<script>window.location.href='{$uri}'</script>");
exit();
}
}
<?php
class Router {
protected $uri;
protected $controller;
protected $action;
protected $params;
protected $route;
protected $method_prefix;
/**
*
* #return mixed
*/
function getUri() {
return $this->uri;
}
/**
*
* #return mixed
*/
function getController() {
return $this->controller;
}
/**
*
* #return mixed
*/
function getAction() {
return $this->action;
}
/**
*
* #return mixed
*/
function getParams() {
return $this->params;
}
function getRoute() {
return $this->route;
}
function getMethodPrefix() {
return $this->method_prefix;
}
public function __construct($uri) {
$this->uri = urldecode(trim($uri, "/"));
//defaults
$routes = Config::get("routes");
$this->route = Config::get("default_route");
$this->controller = Config::get("default_controller");
$this->action = Config::get("default_action");
$this->method_prefix= isset($routes[$this->route]) ? $routes[$this->route] : '';
//get uri params
$uri_parts = explode("?", $this->uri);
$path = $uri_parts[0];
$path_parts = explode("/", $path);
if(count($path_parts)){
//get route
if(in_array(strtolower(current($path_parts)), array_keys($routes))){
$this->route = strtolower(current($path_parts));
$this->method_prefix = isset($routes[$this->route]) ? $routes[$this->route] : '';
array_shift($path_parts);
}
//get controller
if(current($path_parts)){
$this->controller = strtolower(current($path_parts));
array_shift($path_parts);
}
//get action
if(current($path_parts)){
$this->action = strtolower(current($path_parts));
array_shift($path_parts);
}
//reset is for parameters
//$this->params = $path_parts;
//processing params from url to array
$aParams = array();
if(current($path_parts)){
for($i=0; $i<count($path_parts); $i++){
$aParams[$path_parts[$i]] = isset($path_parts[$i+1]) ? $path_parts[$i+1] : null;
$i++;
}
}
$this->params = (object)$aParams;
}
}
}
Create folder structure
Setup .htaccess & virtual hosts
Create config class to build config array
Controller
Create router class with protected non static, with getters
Create init.php with config include & autoload and include paths (lib, controlelrs,models)
Create config file with routes, default values (route, controllers, action)
Set values in router - defaults
Set uri paths, explode the uri and set route, controller, action, params ,process params.
Create app class to run the application by passing uri - (protected router obj, run func)
Create controller parent class to inherit all other controllers (protected data, model, params - non static)
set data, params in constructor.
Create controller and extend with above parent class and add default method.
Call the controller class and method in run function. method has to be with prefix.
Call the method if exisist
Views
Create a parent view class to generate views. (data, path) with default path, set controller, , render funcs to
return the full tempalte path (non static)
Create render function with ob_start(), ob_get_clean to return and send the content to browser.
Change app class to parse the data to view class. if path is returned, pass to view class too.
Layouts..layout is depend on router. re parse the layout html to view and render
Please check this:
<?php
global $conn;
require_once("../config/database.php");
require_once("../config/model.php");
$conn= new Db;
$event = isset($_GET['event']) ? $_GET['event'] : '';
if ($event == 'save') {
if($conn->insert("employee", $_POST)){
$data = array(
'success' => true,
'message' => 'Saving Successful!',
);
}
echo json_encode($data);
}
if ($event == 'update') {
if($conn->update("employee", $_POST, "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Update Successful!',
);
}
echo json_encode($data);
}
if ($event == 'delete') {
if($conn->delete("employee", "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Delete Successful!',
);
}
echo json_encode($data);
}
if ($event == 'edit') {
$data = $conn->get("select * from employee where id={$_POST['id']};")[0];
echo json_encode($data);
}
?>