I have a base controller as follows
<?php
use Phalcon\Mvc\Controller;
class ControllerBase extends Controller {
public function initialize() {
}
// wrapper function for debug purposes.
public function pr($data = null) {
echo '<pre>';
print_r($data);
echo '</pre>';
}
}
and a users controller as follows
<?php
use Phalcon\Mvc\Model\Criteria;
use Phalcon\Paginator\Adapter\Model as Paginator;
use Phalcon\Mvc\View;
class UsersController extends ControllerBase {
public function initialize() {
// initialize parent, here ControllerBase.
parent::initialize();
}
public function loginAction() {
// disable the main layout.
$this->view->disableLevel(View::LEVEL_MAIN_LAYOUT);
// disable the controller layout.
$this->view->disableLevel(View::LEVEL_LAYOUT);
}
.
.
.
.
other functions...
}
i was wondering if i could call all the required phalcon classes in base controller and extend then to all the child classes so that i dont need to call them individually on each controller.
in otherwords, can i add the below code
use Phalcon\Mvc\Model\Criteria;
use Phalcon\Paginator\Adapter\Model as Paginator;
use Phalcon\Mvc\View;
only in the base controller and acces them in other controllers. I tried putting them base controller but it gave error : Class not found.
Is this the right way or is there something wrong in my approach...please help.
If I understand your question correctly the answer is NO.
Namespaces are language feature and works this way. The use Phalcon\Mvc\Model\Criteria only declares that you'll use Criteria class from Phalcon\Mvc\Model\ namespace. So in your code you can write new Criteria() to create object instead of using its' full name new \Phalcon\Mvc\Model\Criteria().
You must declare each class in every file which instantiates object of that class so autoloader will know in which file given class exists.
Related
TL;DR
In my Laravel App, I have a list of controllers named A, B, C, etc (some of the are located in my vendor), which extend from a parent controller named P (located in the vendor) and use controller P's constructor. How can I add a condition Q (if-statement) to my P controller's constructor from outside.
Long Story
In my Laravel App (Backpack for Laravel), I have a list of controllers named PostCrudController, CategoryCrudController, CommentCrudController, etc (some of the are located in my vendor), which extend from a parent controller named CrudController (located in the vendor) and use CrudController's constructor.
PostCrudController, CategoryCrudController, CommentCrudController, etc's structure:
<?php
namespace App\Http\Controllers\Admin;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
class PostCrudController extends CrudController
{
use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
public function setup()
{
CRUD::setModel(\App\Models\Post::class);
CRUD::setRoute(config('backpack.base.route_prefix') . '/post');
CRUD::setEntityNameStrings(trans('general.post'), trans('general.posts'));
}
...
...
...
}
CrudController's structure:
<?php
namespace Backpack\CRUD\app\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class CrudController extends Controller
{
use DispatchesJobs, ValidatesRequests;
public $crud;
public $data = [];
public function __construct()
{
if ($this->crud) {
return;
}
// ---------------------------
// Create the CrudPanel object
// ---------------------------
// Used by developers inside their ProductCrudControllers as
// $this->crud or using the CRUD facade.
//
// It's done inside a middleware closure in order to have
// the complete request inside the CrudPanel object.
$this->middleware(function ($request, $next) {
$this->crud = app()->make('crud');
$this->crud->setRequest($request);
$this->setupDefaults();
$this->setup();
$this->setupConfigurationForCurrentOperation();
return $next($request);
});
}
...
...
...
}
I want to add a condition Q (if-statement) to all my PostCrudController, CategoryCrudController, CommentCrudController, etc controllers. This is the discussed if-statement:
if (!backpack_user()->hasAnyPermission(['post.create']))
{
$this->crud->denyAccess('create');
}
Is it possible to inject/add this condition Q to the parent controller (CrudController) constructor, rather than adding this condition to all the controllers's setup method? (I tested, and this condition works in both cases)
What i've tried so far
I know that these solutions work:
I can simply add this Q condtion to my PostCrudController, CategoryCrudController, CommentCrudController, etc controller's setup method, or to the vendor's blade files.
This Q condition has its functionality if I put that inside my CrudController's construct_ method
But these solutions doesn't seem too be smart to me.
What I've tried unsuccessfully so far to reach to a better solution:
Was defining a controller HasPermissionController, added the condition Q to its constructor, and bound it to the CrudController, in the AppServiceProvider. Which didn't work, as expected: because CrudController isn't requested directly.
HasPermissionController's structure:
<?php
namespace App\Http\Controllers;
class HasPermissionController
{
public function __construct()
{
if (!backpack_user()->hasAnyPermission(['post.create']))
{
$this->crud->denyAccess('create');
}
}
}
AppServiceProvider's structure:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
//
}
public function boot()
{
$this->app->bind(\Backpack\CRUD\app\Http\Controllers\CrudController::class, \App\Http\Controllers\HasPermissionController::class);
}
}
Appreciate any suggestions?
P.S. I already defined a middleware for all my routes to check if the user has the authority to access the route. The job of this $this->crud->denyAccess('create'); condition is to check for example if the user does have authority to the crud's list, but doesn't have authority to that crud's create, simply assigns a false access for the crud's create, which results in not showing the create button inside the list page (backpack uses this strategy inside its blade files to show or hide the buttons).
I am not sure I fully understood your question, but can you create an App\CrudController that extends Backpack\CrudController, and in CategoryCrudController you extend you App\CrudController and not the package Backpack\CrudController.
You can then override the setup() in your App\CrudController.
Cheers
public function create() { $Gallery= GalleryModel::all(); return view('admin.gallery',compact('Gallery')); }
public function video() { $Video= VideoModel::all(); return view('admin.video',compact('Video')); }
public function team() { $Team = TeamModel::all(); return view('admin.team',compact('Team')); }
Is where is any way to make single method for different pages
There are many of ways to do that. The general idea is to use Traits.
// ControllerTrait.php
trait ControllerTrait {
// Here you include all yor controller's methods
}
Then, in every controller you use your trait
// YourController.php
class YourController extends Controller {
use ControllerTrait;
// The rest of your controller's code
}
You can even go further if you consider classes inheritance:
You could create a base controller like this:
// MyBaseController.php
class BaseController extends Controller {
use ControllerTrait; // <= you can mix the strategies for more power
// All your BaseController's methods
}
An then let your controllers extend from the base one instead:
// YourController.php
class YourController extends BaseController {
//
}
You could, in the end, just edit you BaseController and your ControllerTrait and ease your life a lot.
NOTE: Editted because you werer talking about Controllers and I was thinking about Models, but you can surely use the same principle for managing your models.
NOTE 2: Traits are better for methods that really will work as default for all controllers, and keep in the BaseModel those that will need to be customized in any of the inheriting controllers. For example:
// MyBaseController.php
class BaseController extends Controller {
protected function a($request) {
//
}
}
// YourController.php
class YourController extends BaseController {
protected function a($request) {
$request = $modifiedRequest; // You can modify the inputs
parent::a($request); // <== Execute the parent method;
}
}
I'm using the latest 'master' branch of CodeIgniter 4
I have a Library that I'm trying to load automatically. Effectively, I want to have have 'one' index.php (that has meta, the basic html structure, etc) through which I can load views via my 'Template' Library.
My Library file: (~/app/Libraries/Template.php)
//class Template extends CI_Controller
class Template {
/* This throws an error, but I will open up a separte thread for this
public function __construct() {
parent::__construct();
}
*/
public function render($view, $data = array()) {
$data['content_view'] = $view;
return view('layout/index', $data);
}
}
I also have a controller set up:
class Locations extends BaseController
{
public function index()
{
return $this->template->render("locations/index", $view_data);
//return view('locations/index');
}
//--------------------------------------------------------------------
}
In ~/app/Config/ I added my Library
$classmap = [
'Template' => APPPATH .'/Libraries/Template.php'
];
I'm getting the following error:
Call to a member function render() on null
What am I doing wrong that's causing my library not to load?
In CI4 the BaseController is where you create things that you want to be used by multiple other controllers. Creating classes that extend others is so very easy in CI4.
It seems to me that the only thing you are missing is creating the Template class. (There are a couple of other minor things too, but who am I to point fingers?)
One big item that might be just that you don't show it even though you are doing it. That is using namespace and use directives. They are must-do items for CI 4.
Because of where you have put your files you don't need and should remove the following. See how I've used use which imports namespace already known to the autoloader.
$classmap = [
'Template' => APPPATH .'/Libraries/Template.php'
];
First, the BaseController
/app/Controllers/BaseController.php
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use App\Libraries\Template;
class BaseController extends Controller
{
/**
* An array of helpers to be loaded automatically upon
* class instantiation. These helpers will be available
* to all other controllers that extend BaseController.
*
* #var array
*/
protected $helpers = [];
protected $template;
/**
* Constructor.
*/
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
$this->template = new Template();
}
}
/app/Controllers/Locations.php
class Locations extends BaseController
{
public function index()
{
// set $viewData somehow
$viewData['someVar'] = "By Magic!";
return $this->template->render("locations/index", $viewData);
}
}
/app/Libraries/Template.php
<?php namespace App\Libraries;
class Template
{
public function render($view, $data = [])
{
return view($view, $data);
}
}
/app/Views/locations/index.php
This works as if... <strong><?= $someVar; ?></strong>
I know I haven't created exactly what you want to do. But the above should get you where you want to go. I hope so anyway.
It's tricky at first.
But I managed to run it successfully
Make sure you give it proper namespace
And then just "use" in your controller Location.
I dont change anything on Autoload.php.
app/Libraries/Template.php
<?php
namespace App\Libraries;
class Template {
public static function render($param) {
return 'Hello '.ucwords($param);
}
}
The proper way to call is put use App\Libraries\Template just before class Location extends BaseController
app/Controllers/Locations.php
<?php
namespace App\Controllers;
use App\Libraries\Template;
class Locations extends BaseController {
public function index() {
$template = new Template();
$renderedStuff = $template->render('World!');
echo $renderedStuff;
}
}
How does this work?
Notice in Template.php there is a namespace namespace App\Libraries;, so CI4 will automatically load that library properly also recognize the "Template" class. That is proper way to create CI4 libraries in my point of view.
How do we use that library?
Look at my example of Locations.php and then see this code use App\Libraries\Template;, that's how we call that libraries.
How do we call the function?
Look inside the index() function, here we call class Template using var $template = new Template();.
Then we call render() function in Template library with $template->render('World!');.
Just as simple as that.
I hope thats help, lemme know if it doesnt works. :)
Just a little hint, as my eye was hooked by the CI_Controller part.
You seems to use CI3 syntax within CI4, at least about loading the view, which translates to just:
$data = [
'title' => 'Some title',
];
echo view('news_template', $data);
See the CI3doc vs the CI4doc , the "static page" tutorial.
I'm new to Traits, but I have a lot of code that is repeating in my functions, and I want to use Traits to make the code less messy. I have made a Traits directory in my Http directory with a Trait called BrandsTrait.php. And all it does is call on all Brands. But when I try to call BrandsTrait in my Products Controller, like this:
use App\Http\Traits\BrandsTrait;
class ProductsController extends Controller {
use BrandsTrait;
public function addProduct() {
//$brands = Brand::all();
$brands = $this->BrandsTrait();
return view('admin.product.add', compact('brands'));
}
}
it gives me an error saying Method [BrandsTrait] does not exist. Am I suppose to initialize something, or call it differently?
Here is my BrandsTrait.php
<?php
namespace App\Http\Traits;
use App\Brand;
trait BrandsTrait {
public function brandsAll() {
// Get all the brands from the Brands Table.
Brand::all();
}
}
Think of traits like defining a section of your class in a different place which can be shared by many classes. By placing use BrandsTrait in your class it has that section.
What you want to write is
$brands = $this->brandsAll();
That is the name of the method in your trait.
Also - don't forget to add a return to your brandsAll method!
use App\Http\Traits\BrandsTrait;
class ProductsController extends Controller {
use BrandsTrait;
public function addProduct() {
//$brands = Brand::all();
$brands = $this->brandsAll();
return view('admin.product.add', compact('brands'));
}
}
I'm trying to set up PSR-4 within a new Laravel 4 application, but I'm getting some troubles achieving what I want when it comes to build controllers.
Here's what I have now :
namespace MyApp\Controllers\Domain;
class DomainController extends \BaseController {
public $layout = 'layouts.default';
public function home() {
$this->layout->content = \View::make('domain.home');
}
}
I'm not so fond of using \View, \Config, \Whatever to use Laravel's classes. So I was wondering if I could put a use Illuminate\View; to be able to use View::make without putting a \.
Unfortunately, while doing this, I'm getting the following error : Class 'Illuminate\View' not found.
Could somebody help with this please ?
The problem in your case is that View is not located in Illuminate namespace but in Illuminate\View namespace, so correct import would be not:
use Illuminate\View;
but
use Illuminate\View\View;
You can look at http://laravel.com/api/4.2/ to find out which namespace is correct for class you want to use
Assuming BaseController.php has a namespace of MyApp\Controllers\Domain
namespace MyApp\Controllers\Domain;
use View;
class DomainController extends BaseController {
public $layout = 'layouts.default';
public function home() {
$this->layout->content = View::make('domain.home');
}
}
If BaseController.php has other namespace, i.e MyApp\Controllers
namespace MyApp\Controllers\Domain;
use MyApp\Controllers\BaseController;
use View;
class DomainController extends BaseController {
public $layout = 'layouts.default';
public function home() {
$this->layout->content = View::make('domain.home');
}
}
If, for instance, you controller needs to use another base class from Laravel, lets say Config.
namespace MyApp\Controllers\Domain;
use MyApp\Controllers\BaseController;
use View;
use Config;
class DomainController extends BaseController {
public $layout = 'layouts.default';
public function home() {
$this->layout->content = View::make('domain.home')->withName(Config::get('site.name'));
}
}
The use of View::make() takes advantage of the Laravel facades. To properly reference the facade, instead of directly referencing the class that gets resolved out of the iOC container, I would use the following:
use Illuminate\Support\Facades\View;
This will reference the View facade that is being used when calling View::make()