Laravel: "override" request object? - php

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';
}

Related

want to pass variable to aftersave() method after save(false) in yii

Actually,
as you see below code,
in model two functions are calling
$this->sendMailtoTutor(),$this->sendMailtoLearner()
i want to check a condition in model that if that condition will true then only these both function will call otherwise not. Is there any way to pass attributes or variable so that i can check condition in aftersave().
my code is as below
in controller
public function actionBooking()
{
$booking_temp = new BookingTemp();
$this->performAjaxValidation($booking_temp);
if (Yii::app()->request->isPostRequest && Yii::app()->request->getPost('BookingTemp'))
{
$booking_temp->attributes = Yii::app()->request->getPost('BookingTemp');
//echo '<pre>';print_r($booking_temp->attributes);exit;
if ($booking_temp->validate())
{
$extra_price = 0;
$post_data = Yii::app()->request->getPost('BookingTemp');
$cam = Cam::model()->findByPk($post_data['temp_cam_id']);
$data = array();
$data = $post_data;
$data['temp_book_user_id'] = Yii::app()->user->id;
$data['temp_book_cam_price'] = $cam->cam_price;
$data['temp_book_duration'] = $cam->cam_duration;
if ($post_data['temp_book_session'] == 2) {
$data['temp_book_cam_price'] = 2 * $cam->cam_price;
$data['temp_book_duration'] = 2 * $cam->cam_duration;
}
if ($post_data['temp_book_is_extra'] == "Y") {
$extra_price = $cam->camExtras->extra_price;
$data['temp_book_extra_price'] = $extra_price;
}
$price_calculation = CamBooking::price_calculation(Yii::app()->user->country_id, $data['temp_book_cam_price'], $extra_price);
$data['temp_book_processing_fees'] = $price_calculation['processing_fees'];
$data['temp_book_service_tax'] = $price_calculation['service_tax'];
$data['temp_book_total_price'] = $price_calculation['total_price'];
$booking_temp->temp_value = serialize($data);
$booking_temp->user_id = Yii::app()->user->id;
$booking_temp->tutor_id = $cam->tutor_id;
$booking_temp_variable = 'check_aftersave';
$booking_temp->save(false, $booking_temp_variable);
//$booking_temp->saveAttributes(array('tutor_id', 'user_id', 'temp_value'));
$created_at = Yii::app()->localtime->fromUTC($booking_temp->created_at);
$created_at_time = strtotime($created_at);
$end_time = $created_at_time + (60 * 3); // 3 min greater from created
$end_time_format = date("Y/m/d H:i:s", $end_time);
echo json_encode(array(
'status' => 'success',
'temp_guid' => $booking_temp->temp_guid,
'end_time_format' => $end_time_format,
), JSON_UNESCAPED_SLASHES);
Yii::app()->end();
} else {
$error = CActiveForm::validate($booking_temp);
if ($error != '[]')
echo $error;
Yii::app()->end();
}
}
}
in model
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->sendMailtoTutor();
$this->sendMailtoLearner();
if ($this->is_message == 'Y' && !empty($this->book_message))
{
Message::insertMessage($this->book_message, $this->book_user_id, $this->cam->tutor_id, $this->cam_id);
}
$user_profile_link = CHtml::link($this->bookUser->fullname, array("/site/user/profile", "slug" => $this->bookUser->slug));
$cam_link = CHtml::link($this->cam->cam_title, array("/site/cam/view", "slug" => $this->cam->slug));
$message = "You have a new booking from {$user_profile_link} for your {$cam_link}";
Notification::insertNotification($this->cam->tutor_id, $message, 'book', $this->book_id);
}
return parent::afterSave();
}
How can I perform this?
Hi See if you want to do it in the afterSave() of BookingTemp Model;. Then you can define a variable in that model like
like
class BookingTemp extends CActiveRecord
{
public $booking_temp_variable;
............
}
now assign this variable before saving your model in actionBooking();
as
$booking_temp->booking_temp_variable = 'check_aftersave';
$booking_temp->save(false);// no need to pass a varible
now in your aftersave function you can easily get it like
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->booking_temp_variable; // this has your value
}
return parent::afterSave();
}
Don't forget to keep it in rules as safe
public function rules(){
// your rules
array('booking_temp_variable','safe');
}
What exacly you want to do?
At first you have to know, that when you are calling save() method and u pass there two args - false and, $booking_temp_variable . The first arg says that you do not run validation, the second one is (should be) array of attributes.
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#save-detail

How can I use callback functions in groceryCrud for the view record page?

I do not know how to set a callback function for the view record page in codeigniter.
I use the callback_column function and it does what I need in the grid view, but on the view record page it does not work.
I searched their site and forum and did not found anything that could help me.
My code looks like:
$zeus = new grocery_CRUD();
$zeus->set_theme('bootstrap');
// $zeus->set_language('romanian');
$zeus->set_table('programari');
$zeus->columns(array('id_client', 'id_sala', 'denumire', 'numar_persoane', 'observatii'));
$zeus->callback_column('id_sala',array($this,'_test_function'));
$cod = $zeus->render();
$this->_afiseaza_panou($cod);
public function _test_function($row, $value)
{
return '0';
}
write this lines in \libraries\Grocery_CRUD.php
at line number 3530
protected $callback_read_field = array();
than put this function after constructor call
public function callback_read_field($field, $callback = null)
{
$this->callback_read_field[$field] = $callback;
return $this;
}
//Now update this function to manage the field outputs using callbacks if they are defined for the same
protected function get_read_input_fields($field_values = null)
{
$read_fields = $this->get_read_fields();
$this->field_types = null;
$this->required_fields = null;
$read_inputs = array();
foreach ($read_fields as $field) {
if (!empty($this->change_field_type)
&& isset($this->change_field_type[$field->field_name])
&& $this->change_field_type[$field->field_name]->type == 'hidden') {
continue;
}
$this->field_type($field->field_name, 'readonly');
}
$fields = $this->get_read_fields();
$types = $this->get_field_types();
$input_fields = array();
foreach($fields as $field_num => $field)
{
$field_info = $types[$field->field_name];
if(isset($field_info->db_type) && ($field_info->db_type == 'tinyint' || ($field_info->db_type == 'int' && $field_info->db_max_length == 1))) {
$field_value = $this->get_true_false_readonly_input($field_info, $field_values->{$field->field_name});
} else {
$field_value = !empty($field_values) && isset($field_values->{$field->field_name}) ? $field_values->{$field->field_name} : null;
}
if(!isset($this->callback_read_field[$field->field_name]))
{
$field_input = $this->get_field_input($field_info, $field_value);
}
else
{
$primary_key = $this->getStateInfo()->primary_key;
$field_input = $field_info;
$field_input->input = call_user_func($this->callback_read_field[$field->field_name], $field_value, $primary_key, $field_info, $field_values);
}
switch ($field_info->crud_type) {
case 'invisible':
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
case 'hidden':
$this->read_hidden_fields[] = $field_input;
unset($this->read_fields[$field_num]);
unset($fields[$field_num]);
continue;
break;
}
$input_fields[$field->field_name] = $field_input;
}
return $input_fields;
}
than call same as other callback functions
As far as I'm aware GroceryCRUD doesn't provide callbacks or another means of overriding the default output in the view state.
The solution to customising this would be to create a custom view to which you will insert the data from your record. This way you can customise the layout and other presentation.
What you would then do is unset the default read view with:
$crud->unset_read();
And add a new action where there are details on how to do this here.
What to do with the new action is point it to a URL that you map in routes.php if necessary and handle it with a new function in your controller. You'll either have to write a model function to retrieve the data since this isn't passed from GC or you can use the action to target a callback and feed $row to it via POST or something so that the data for the record is accessible in the view. (Look at the example in the link above).

Creating default object from empty value (adding an item to a database for a new user in Laravel)

I have a function I am using to add
I have an array $data that contains the user data I am trying to put into the db. Everything works except for the "makeUrlTag" function portion:
public function makeUrlTag() {
$url_tag = '';
if(isset($this->data['user']['first_name'])) {
$url_tag = $url_tag . $this->data['user']['first_name'];
}
if(isset($this->data['user']['last_name'])) {
$url_tag = $url_tag.$this->data['user']['last_name'];
}
$fan->url_tag = $url_tag;
}
public function createFan() {
$fan = new Fan;
$fan->fbid = isset($this->data['user']['id']) ? $this->data['user']['id'] : '';
$fan->email = isset($this->data['user']['email']) ? $this->data['user']['email'] : '';
$fan->first_name = isset($this->data['user']['first_name']) ? $this->data['user']['first_name'] : '';
$fan->last_name = isset($this->data['user']['last_name']) ? $this->data['user']['last_name'] : '';
$this->makeUrlTag();
$fan->save();
}
I call createFan with:
$this->createFan();
When I run this, I get the error:
Creating default object from empty value
in reference to the makeUrlTag(); portion. Particularly the line:
$fan->url_tag = $url_tag;
Any idea what's going on here? Again, taking out the makeUrlTag portion works fine. Thank you.
It's because your makeUrlTag() method doesn't know about the Fan which is in the $fan variable you created in the createFan() method and trying to use a non-existing object in the scope of makeUrlTag() method using this:
$fan->url_tag = $url_tag;
So, you need to make your $fan object available to makeUrlTag() and to do this you may add a protected property in your class:
class YourClass {
protected $fan = null;
public function makeUrlTag(){
$url_tag = '';
// ...
$this->fan->url_tag = $url_tag;
}
public function makeUrlTag(){
$this->fan = new Fan;
// rest of your code
// but use $this->fan instead of $fan
$this->fan->save();
}
}
So, now you can access the $fan object from any method of your class ussing $this->fan, that's it.

Auto mapping method parameters to $_POST data (MVC)

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!

An example of an MVC controller

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

Categories