Voyager - laravel admin panel - laravel-routing

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

Related

Authentication views for Laravel 5.1

Laravel 5.1 has just been released, I would like to know how could I tell the AuthController to get the login & register view from a custom directory? the default is: resources/views/auth...
The trait AuthenticateAndRegisterUsers only has this:
trait AuthenticatesAndRegistersUsers
{
use AuthenticatesUsers, RegistersUsers {
AuthenticatesUsers::redirectPath insteadof RegistersUsers;
}
}
The code you're showing there only fills one function: it tells our trait to use the redirectPath from the AuthenticatesUsers trait rather than the one from RegistersUsers.
If you check inside the AuthenticatesUsers trait instead, you will find a getLogin() method. By default, this one is defined as
public function getLogin()
{
return view('auth.login');
}
All you have to do to get another view is then simply overwriting the function in your controller and returning another view. If you for some reason would like to load your views from a directory other than the standard resources/Views, you can do so by calling View::addLocation($path) (you'll find this defined in the Illuminate\View\FileViewFinder implementation of the Illuminate\View\ViewFinderInterface.
Also, please note that changing the auth views directory will do nothing to change the domain or similar. That is dependent on the function name (as per the definition of Route::Controller($uri, $controller, $names=[]). For more details on how routing works, I'd suggest just looking through Illuminate\Routing\Router.
for those who is using laravel 5.2, you only need to override property value of loginView
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
public function showLoginForm()
{
$view = property_exists($this, 'loginView')
? $this->loginView : 'auth.authenticate';
if (view()->exists($view)) {
return view($view);
}
return view('auth.login');
}
so to override the login view path, you only need to do this
class yourUserController {
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
.....
protected $loginView = 'your path';
}

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'])

Is it possible to render View without create Action in ASP.NET MVC?

Is it possible to get/render View without creating Action in Controller? I have many Views where I dont need pass any model or viewbag variables and I thing, its useless to create only empty Actions with names of my Views.
You could create a custom route, and handle it in a generic controller:
In your RouteConfig.cs:
routes.MapRoute(
"GenericRoute", // Route name
"Generic/{viewName}", // URL with parameters
new { controller = "Generic", action = "RenderView", }
);
And then implement a controller like this
public GenericContoller : ...
{
public ActionResult RenderView(string viewName)
{
// depending on where you store your routes perhaps you need
// to use the controller name to choose the rigth view
return View(viewName);
}
}
Then when a url like this is requested:
http://..../Generic/ViewName
The View, with the provided name will be rendered.
Of course, you can make variations of this idea to adapt it to your case. For example:
routes.MapRoute(
"GenericRoute", // Route name
"{controller}/{viewName}", // URL with parameters
new { action = "RenderView", }
);
In this case, all your controllers need to implement a RenderView, and the url is http://.../ControllerName/ViewName.
In my opinion it's not possible, the least you can create is
public ActionResult yourView()
{
return View();
}
If these views are partial views that are part of a view that corresponds to an action, you can use #Html.Partial in your main view to render the partial without an action.
For example:
#Html.Partial("MyPartialName")

How do I make a construct to have beforeAuth only apply to certain views/functions in Laravel 4

I have a resource in Laravel I have called artists with an ArtistsController. I would like to add filters to some of the pages, but not all. I know I can add a filter to all of the functions/views in the resource controller like so:
public function __construct()
{
$this->beforeFilter('auth', array('except' => array()));
}
How do I add the beforeAuth filter to only a certain view/function? I would like a user to be logged in in order to go the "index" view, but I would like a user to be able to go to the "show" pages without necessarily being logged in:
public function index()
{
$artists = Artist::all();
return View::make('artists.index', compact('artists'))
->with('artists', Artist::all())
->with('artists_new', Artist::artists_new());
}
public function show($id)
{
$artist = Artist::find($id);
return View::make('artists.show', compact('artist'))
->with('fans', Fan::all());
}
Is there a way to do this? Thank you.
Not sure if this helps but you could use the only key instead of the except (if I understand your question correctly).
$this->beforeFilter('auth', array('only' => array('login', 'foo', 'bar')));
Although that would still go in the constructor.

Display the login form in the layouts main.php in Yii

I want to change the the "views/layouts/main.php" to display the login form whenever the user isn't authenticated.
So I changed the "siteController" actionIndex like that:
public function actionIndex() {
$loginForm = new LoginForm();
$this->render('index', array('loginForm'=>$loginForm));
}
And then call it in "views/layouts/main.php" like that:
if(Yii::app()->user->isGuest):
echo $loginForm;
else :
echo 'JJJ';
endif;
Then when I go to my website, It display the error: "Undefined variable: loginForm".
I don't know how to fix this? :(
Define new property in your controller class:
public $loginForm;
In your main.php access it like:
echo $this->loginForm;
If you pass variable in your render method it will be available inside view file only, but not in layout file.
It's because the index template is loaded before main template. So, better way to do hat you want, is to define a public property in your Controller. I suggest you to define this property in Controller class because SiteController and *Controller extends it.
Then, you can run this.
if(Yii::app()->user->isGuest) {
echo $this->loginForm;
} else {
echo 'JJJ';
}
Pay attention, because in this way of work you go out MVC pattern. This way of work force you to define a LoginForm in each action. I suggest you to do that:
Leave clean your calls to render file.
public function actionIndex() {
$this->render('index');
}
And add a getLoginForm method in you Controller class obtaining:
if(Yii::app()->user->isGuest) {
echo $this->getLoginForm();
} else {
echo 'JJJ';
}
There are a couple issues here. Firstly, you are creating an object called $loginForm and assigning it a value of new LoginForm();
$loginForm = new LoginForm();
I'm not sure if you are doing this on purpose and LoginForm() is a function or a method that returns something, but I have a feeling you were intending to do:
$loginForm = new LoginForm;
Which creates a new instance of the class LoginForm (which is a default Yii webapp CFormModel class). Even if that is the case, there are better ways to do this.
The easiest way is to call a renderPartial of the already existing login.php view (located in protected/views/site/login.php) inside your index.php view like so:
if(Yii::app()->user->isGuest) {
$this->renderPartial("loginform",array("model"=>new LoginForm));
} else {
echo 'JJJ';
}
This renders the view login.php (without rendering the layout because we have already rendered the layout - here's the docs on render and renderPartial) and pass it a new instance of the model LoginForm assigned to a variable called $model.
You will most likely have to edit the look of login.php view to make it "fit", but keep in mind that this view is being used in the SiteController actionLogin as well.
All that's left to do then is modify your actionIndex to handle the form submission (you can just copy the existing SiteController actionLogin functionality)
Another nicer solution would be to create a widget for the login form which can be used all over your application. I'm not going to go into that, but you can read up about it here on SO or check out this tutorial or this one.