call action of another controller - yii

i have a grid view and i would like to get value of column from another action controller.
at now i have this in controller 1
array(
'name'=>'title',
'value'=>array($this,'Action2'),
),
and i get this error:
controller1 and its behaviors do not have a method or closure named "Action2".
if i replace $this with "controller2"
array(
'name'=>'title',
'value'=>array('controller2','Action2'),
),
i get this error
call_user_func_array() [<a href='function.call-user-func-array'>function.call-user-func-array</a>]: First argument is expected to be a valid callback, 'controller2::action2' was given
maybe this is bad practice but is this feasible?

It's bad practice to use controller actions this way. Better place your code in model's method. But if you still want to do this, here is one way:
'value' => function() {
list($controller) = Yii::app()->createController('controllerId');
return $controller->actionTest();
}
Here is another:
'value' => function() {
$controller = new TestController('test');
return $controller->actionTest();
}

You can use this solution:
Yii::app()->runController('category/view/id/1');

Related

Can't insert into database with save()

I am having an issue inserting a record into the database. I am a beginner with the Yii framework, so I may have made some stupid mistakes.
This is from the SiteController
public function actionCreatePost(){
$model = new PostForm();
$post = new Post();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$post->body = $model->body;
$post->title = $model->title;
$post->save();
return $this->redirect('index');
}else {
return $this->render('createPost', ['model' => $model]);
}
}
This is from the Post class
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => new Expression('NOW()'),
],
[
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'id_author',
]
];
}
The issue is that you have created a PostForm class for the form (which is correct) but you are then trying to load the response into the Post class - a completely different class. This won’t work without modification.
If you have a look at the response:
var_dump(Yii:$app->request->post());
You will see the form data is located within the PostForm key. Yii will therefore only load the data into the PostForm class.
The correct solution is therefore to create a savePost() function within the PostForm eg:
public function savePost(){
$model = new Post();
$model->propertyABC = $this->propertyABC
...etc...
$model->save();
So the action would appear as follows:
$model = new PostForm();
If($model->load(Yii::$app->request->post()) && $model->validate()){
$model->savePost();
The other option is to rename the key from PostForm to Post. Yii will then load the data but this is not the best approach as it is a bit obscure.
Hope that helps
I would guess the issue is with the validation.
I can see several issues I will point out. First, I cannot figure out why are you creating a new PostForm, loading the data in it and verifying it, just to dump some values in a new Post and save it. Are there some functions, you are running in the PostForm model, that are triggered by load or verify? If that is not the case, I would suggest dropping one of the models, and using only the other. Usually, that is the Form model. It serves as a link between the ActiveForm and the model handling everything. You can do everything in the createPost() function in the Form model, and then in the controller it will look like
if ($model->load(Yii::$app->request->post())) {
$model->save();
return $this->redirect('index');
}
Second of all, you can dump post->getErrors() before the save to see if there are any errors with the validation. What you can also do, is call $post->save(false) instead. If you pass false to it, it will not trigger $post->validate(), and some errors can be neglected. Please, let me know if there is anything unclear.

Phalcon router sends the wrong parameters

I have a problem with my router in Phalcon.
I have an action in my controller which ether takes a date parameter or not.
So when I access an URL: http://example.com/sl/slots/index/2017-06-27
everything works ok.
But when I go to: http://example.com/sl/slots/index
I get the following error:
DateTime::__construct(): Failed to parse time string (sl) at position
0 (s): The timezone could not be found in the database.
So the router actually takes the "sl" in the beginning as a parameter.
My router for this kind of url is set like this:
$router->add(
"/{language:[a-z]{2}}/:controller/:action",
array(
"controller" => 2,
"action" => 3
)
);
Btw it does the same withut the index: http://example.com/sl/slots
Oh and my slots index action looks like this:
public function indexAction($currentDate = false){ //code }
So the $currentDate is set to "sl" when I call the action without a parameter
Thank you for the help
Well you need to add language in first argument of action too. Then it should work.
In addition to #Juri's answer.. I prefer to keep my Actions empty or as slim as possible. Imagine if you have 3-4 parameters in the Route, you will end up with something like:
public function indexAction($param1 = false, $param2 = false, $param3 = false....)
Here is how I prefer to handle Route parameters:
public function indexAction()
{
// All parameters
print_r($this->dispatcher->getParams());
// Accessing specific Named parameters
$this->dispatcher->getParam('id');
$this->dispatcher->getParam('language');
// Accessing specific Non-named parameters
$this->dispatcher->getParam(0);
$this->dispatcher->getParam(1);
...
}

Voyager - laravel admin panel

My question is related to controller in Voyager admin panel. For example I created a table with migration . It's name was "groups" and then I created BREAD and added it to menu in Voyager.
I created a folder that it's name is "groups" in \resources\views\vendor\voyager andthen I created two file to override the view.
But I do not know where the controller is . I created controller with php artisan make:controller GroupsController. I guess this controller is not related to voyager controllers.
I want to change the index or create method and pass some data to views in controller but I do not know where it is.
I created a controller in \vendor\tcg\voyager\src\Http\Controllers that it's name is VoyagerGroupsController.php but when I create class and index method in it , it does not work.
How can I create controller for "groups" and pass the data to the view?
Whenever we create a table in voyager, Voyager calls it datatype. And for all tables / datatypes created by us, Voyager users only one controller VoyagerBreadController.php located at **vendor\tcg\voyager\src\Http\Controllers**.
For example, if I create a table named brands. Laravel will use controller VoyagerBreadController.
But where are the routes which use or point to this controller. Routes are located in file vendor\tcg\voyager\routes\voyager.php. In this file, find the following lines:
try {
foreach (\TCG\Voyager\Models\DataType::all() as $dataTypes) {
Route::resource($dataTypes->slug, $namespacePrefix.'VoyagerBreadController');
}
} catch (\InvalidArgumentException $e) {
throw new \InvalidArgumentException("Custom routes hasn't been configured because: ".$e->getMessage(), 1);
} catch (\Exception $e) {
// do nothing, might just be because table not yet migrated.
}
In my version, these lines are between line No. 29 to 37.
As you can see, above code is fetching all our datatypes and creating a resouce route for our tables / datatypes.
Now, if I want to override this route and create a route to use my own controller for a particular action. For example, if I want to create a route for brands/create url. I can do this by simply adding following line (my route) below above code (i.e. after line 37):
Route::get('brands/create', function(){return 'abc';})->name('brands.create');
or you can do the same by adding following line in routes\web.php after Voyager::routes();
Route::get('brands/create', function(){return 'abc';})->name(**'voyager.brands.create'**);
Because it's now it's using your App controller not a Voyager controller so you have to override your full controller
like
In config/voyager.php add
'controllers' => [
'namespace' => 'App\\Http\\Controllers',
],
Create new controller like MyBreadController.php into App/controller
<?php
namespace App\Http\Controllers;
class MyBreadController extends \TCG\Voyager\Http\Controllers\Controller
{
//code here
}
app/Providers/AppServiceProvider.php
use TCG\Voyager\Http\Controllers\VoyagerBreadController;
use App\Http\Controllers\MyBreadController;
public function register()
{
$this->app->bind(VoyagerBreadController::class, MyBreadController::class);
//
}
I added Route::get('groups', 'GroupsController#index') as you said in routes/web.php
like this
Route::group(['prefix' => 'admin'], function () {
Voyager::routes();
Route::get('groups', 'GroupsController#index');
});
and added these lines in index method
public function index(Request $request){
// GET THE SLUG, ex. 'posts', 'pages', etc.
$slug = $this->getSlug($request);
// GET THE DataType based on the slug
$dataType = DataType::where('slug', '=', $slug)->first();
// Check permission
Voyager::can('browse_'.$dataType->name);
// Next Get the actual content from the MODEL that corresponds to the slug DataType
$dataTypeContent = (strlen($dataType->model_name) != 0)
? app($dataType->model_name)->latest()->get()
: DB::table($dataType->name)->get(); // If Model doest exist, get data from table name
$view = 'voyager::bread.browse';
if (view()->exists("voyager::$slug.browse")) {
$view = "voyager::$slug.browse";
}
return view($view, compact('dataType', 'dataTypeContent'));
}
But getSlug method does not work. This error will be shown
ErrorException in GroupsController.php line 23:
Trying to get property of non-object
I guess after overriding Controlles getSlug() does not work and I have to set the slug manually
$slug = 'groups';

Yii - Calling api request within CConsole

I have the following issue:
I have a local db with comments and I need to do some actions with some users I'm getting via an API request.
Code is something like this:
class RunCronCommand extends CConsoleCommand {
public function actionIndex() {
...
$comments = Comment::model()->findAll('status = :status', array(':status' => Comment::ACTIVE));
foreach ($comments as $comment) {
$profile = Yii::app()->api->get('/users/'. $comment->user_id . '/getProfile');
}
...
}
When I execute the command I'm getting this error
exception 'CException' with message 'Property "CConsoleApplication.api" is not defined.' in /var/www/core/trunk/common/lib/Yii/base/CComponent.php:131
Any thoughts?
thanks in advance!
Thank you for your reply.
Got it fixed by adding the class into console/config/main.php
'components' => array(
'api' => array(
'class' => 'common.extensions.Api.Api'
),
You will have to rethink the design of the application.
From your example, you might want to use Curl, although I think this might be overkill/
To run an action in a controller, you can use something like
Yii::import('application.modules.moduleName.controllers.ControllerName');
$controller_instance = new ControllerName("Default");
$controller_instance->actionIndex();
Look here for additional information.

Laravel dynamic return from controller?

I need to return a different view from a controller depending on the route that is requested.
For example: in my application I have clients, devices and campaigns. All have CRUD's created, but in some cases I want to do something like "view clients, delete his campaign and returning to the clients view" but my campaignsController#delete returns to campaigns by default.
The idea is not rewrite the same controller only changing the route of returning, does Laravel have something to help with this?
Thank you
Laravel will not control the whole flow of your application. If you have a campaign delete router:
Route::delete('campaign/{id}');
and it returns to campaigns
class CampaignController extends Controller {
public function delete($id)
{
$c = Campaign::find($id);
$c->delete();
return Redirect::route('campaigns');
}
}
You will have to trick your route to make it go to wherever you need to, there should be dozens of ways of doing that, this is a very simple one:
Route::delete('campaign/{id}/{returnRoute?}');
class CampaignController extends Controller {
public function delete($id, $returnRoute = null)
{
$returnRoute = $returnRoute ?: 'campaigns';
$c = Campaign::find($id);
$c->delete();
return Redirect::route($returnRoute);
}
}
And create the links in those pages by using the return route option:
link_to_route('campaign.delete', 'Delete Campaign', ['id' => $id, 'returnRoute' => 'clients'])