How can I extend class Confide in Laravel 4? - php

I want to extend/overwrite the method logAttempt in class Confide (Confide on GitHub) in order to execute some extra code whenever someone logs in successfully. This would be cleaner than copying the same code to all controllers where logAttempt is called.
I read through the Laravel documentation and several answers here on stackoverflow, but I just can't get it working.
I created a new folder app/extensions with a file named Confide.php:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function __construct(ConfideRepository $repo) {
die('no way!');
$this->repo = $repo;
$this->app = app();
}
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
die('yeah man!');
}
}
I added the directory to my app/start/global.php:
ClassLoader::addDirectories(array(
// ...
app_path().'/extensions',
));
I also added it to composer.json and ran composer dump-autoload:
"autoload": {
"classmap": [
...,
"app/extensions"
]
},
My own Confide class seems not to be loaded at all, because Confide works as normal – without ever die()-ing.
And if I use \Extensions\Confide::logAttempt($input, true); in my controller including the namespace, I get this ErrorException:
Non-static method Extensions\Confide::logAttempt() should not be called statically, assuming $this from incompatible context
Do I really need my own ConfideServiceProvider class as well? I tried that, too, but I'm not sure at all what to put in there to make Confide use my extended class.
Is there no simple way to extend a tiny bit of a class? There must be, I'm just missing something here.

If you are looking to execute some code when a user logs in, you should just listen for that event. In this case, I believe Confide uses the Auth class to login, so you should be able to listen for that event.
Event::listen('auth.login', function($user)
{
$user->last_login = new DateTime;
$user->save();
});
I find this much easier and cleaner than worrying about extending classes.

EDIT: Made a mistake
I think you need to call the method like this:
\Extensions\Confide->logAttempt($input, true);
because you are using:
\Extensions\Confide::logAttempt($input, true);
Which is how you call static methods.

I think I finally figured it out.
I had to extend ConfideServiceProvider as well like so:
<?php
namespace Extensions;
class ConfideServiceProvider extends \Zizaco\Confide\ConfideServiceProvider {
/**
* Bootstrap the service provider.
*
* #return void
*/
public function boot() {
$this->package('extensions/confide');
}
/**
* Register the application bindings.
*
* #return void
*/
protected function registerConfide() {
$this->app->bind('confide', function($app) {
return new Confide($app->make('confide.repository'));
});
}
}
The code above goes into app/extensions/ConfideServiceProvider.php. Note: In boot() I replaced "zizaco" with "extensions" and in registerConfide() I made no changes at all, but if this method is not present in the extended class, the original class will be used. I've got no idea why.
Then in app/config/app.php I replaced Zizaco\Confide\ConfideServiceProvider with Extensions\ConfideServiceProvider.
My own extended Confide class looks like this now:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
$result = parent::logAttempt($credentials, $confirmed_only, $identity_columns);
if ($result) {
// Login successful. Do some additional stuff.
\Log::info('User ' . \Auth::user()->username . ' logged in.');
}
return $result;
}
}
Note: If you want to use any other standard Laravel class like Log, Session etc., prefix it with one backslash as shown in the example above, or add a use operator for each class you use (e.g. use \Log;).

Related

Laravel singleton not working across controller/ViewComposer

In Laravel, I have a class that I would like to make available to the service controller, make some changes to in the controller action, and then render out with a ViewComposer.
I have done this several times before without issue, but for some reason this time my usual approach is not working - clearly I'm doing something different, and I'm beginning to suspect I've fundamentally misunderstood an aspect of what I am doing.
I have a ServiceProvider with this register() method:
public function register()
{
$this->app->singleton(HelperTest::class, function ($app) {
$pb = new HelperTest();
$pb->test = "jokes on you batman";
return $pb;
});
}
Then in my controller I'm doing the following:
private $helper;
public function __construct(HelperTest $pb)
{
$this->helper = $pb;
$this->helper->test = "hahah";
}
And then I have a viewcomposer doing the following:
private $helper;
public function __construct(HelperTest $pb)
{
$this->helper = $pb;
}
public function compose(View $view)
{
$view->with('output', $this->helper->test);
}
When I call {{ $output }} in the blade view, I expect to see hahah, but instead I get jokes on you batman.
My debugging has shown that all three of these methods are definitely being called. It looks to me like the ViewComposer is for some reason instantiating its own, fresh instance of the class. What am I doing wrong?
Thanks!
Execute php artisan optimize on your console, this will generate an optimized class loader for your application, then check if you can find your class HelperTest registered in services.php inside boostrap/cache. Until HelperTest is not registered there, Laravel IoC can't resolve your class.

Unable to use helper in controller of laravel app

I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.

Access app in class in Slim Framework 3

Im having trouble understanding how to access the instance of Slim when a route is in a seperate class than index.php
When using Slim Framework 2 I always used the following, but its not working in Slim 3:
$this->app = \Slim\Slim::getInstance();
Im trying to access a database connection I have setup in the container, but from a separate class. This is what I currently got in my index.php to initiate a Slim app:
require_once("rdb/rdb.php");
$conn = r\connect('localhost');
$container = new \Slim\Container;
$container['rdb'] = function ($c){return $conn;}
$app = new \Slim\App($container);
And here is my route:
$app->get('/test','\mycontroller:test');
And this is what I got in my mycontroller.php class which my route points to, which obviously is not working as $this->app doesn't exist:
class mycontroller{
public function test($request,$response){
$this->app->getContainer()->get('rdb');
}
The error message is the following, due to getinstance not being part of Slim 3 compared to Slim 2:
Call to undefined method Slim\App::getInstance()
Grateful for any help,
Regards
Dan
Have a look at the Slim 3 Skeleton created by Rob Allen.
Slim 3 heavily uses dependency injection, so you might want to use it too.
In your dependencies.php add something like:
$container = $app->getContainer();
$container['rdb'] = function ($c) {
return $conn;
};
$container['Your\Custom\Class'] = function ($c) {
return new \Your\Custom\Class($c['rdb']);
};
And in your Your\Custom\Class.php:
class Class {
private $rdb;
function __construct($rdb) {
$this->rdb = $rdb;
}
public function test($request, $response, $args) {
$this->rdb->doSomething();
}
}
I hope this helps, if you have any more questions feel free to ask.
Update:
When you define your route like this
$app->get('/test', '\mycontroller:test');
Slim looks up \mycontroller:test in your container:
$container['\mycontroller'] = function($c) {
return new \mycontroller($c['rdb']);
}
So when you open www.example.com/test in your browser, Slim automatically creates a new instance of \mycontroller and executes the method test with the arguments $request, $response and $args.
And because you accept the database connection as an argument for the constructor of your mycontroller class, you can use it in the method as well :)
With Slim 3 RC2 and onwards given a route of:
$app->get('/test','MyController:test');
The CallableResolver will look for a key in the DIC called 'MyController' and expect that to return the controller, so you can register with the DIC like this:
// Register controller with DIC
$container = $app->getContainer();
$container['MyController'] = function ($c) {
return new MyController($c->get('rdb'));
}
// Define controller as:
class MyController
{
public function __construct($rdb) {
$this->rdb = $rdb;
}
public function test($request,$response){
// do something with $this->rdb
}
}
Alternatively, if you don't register with the DIC, then the CallableResolver will pass the container to your constructor, so you can just create a controller like this:
class MyController
{
public function __construct($container) {
$this->rdb = $container->get('rdb');
}
public function test($request,$response){
// do something with $this->rdb
}
}
I created the following base controller and extended from that. Only just started playing with Slim but it works if you need access to to the DI in your controllers.
namespace App\Controllers;
use Interop\Container\ContainerInterface;
abstract class Controller
{
protected $ci;
/**
* Controller constructor.
*
* #param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->ci = $container;
}
/**
* #param $name
* #return mixed
*/
public function __get($name)
{
if ($this->ci->has($name)) {
return $this->ci->get($name);
}
}
}
Then in your other controllers you can use it like this.
namespace App\Controllers;
/**
* Class HomeController
*
* #package App\Controllers
*/
class HomeController extends Controller
{
/**
* #param $request
* #param $response
* #param $args
* #return \Slim\Views\Twig
*/
public function index($request, $response, $args)
{
// Render index view
return $this->view->render($response, 'index.twig');
}
}
Important
I upvoted #mgansler and you should read that first if dealing with slim 3, and read this only if interested in differences to slim 2.
Update
So it seems those usages were just old code no one cleaned.
However im leaving this post here as it should be helpful to anyone using Slim 2 (as slim 3 is very much still beta) and as a referance point to help see differences.
Old Update (see above)
Following update of OP, i looked at github source code and found that getInstance is still very much there, but with some slight differences perhaps...
https://github.com/slimphp/Slim/search?utf8=%E2%9C%93&q=getInstance
Test files (which maybe outdated, but unlikely) show something like this:
public function testGetCallableAsStaticMethod()
{
$route = new \Slim\Route('/bar', '\Slim\Slim::getInstance');
$callable = $route->getCallable();
$this->assertEquals('\Slim\Slim::getInstance', $callable);
}
But at the same time we see calls like this in some files, which are obviously contextual and either return diff object ($env) or are in same static file (Slim.php)
$env = \Slim\Environment::getInstance(true);
static::getInstance();
But this does show the static function still exists, so use my examples below and try to figure out why not working for you in current form.
Also, this 'maybe' of interest, as only obvious example of slim3 in usage: https://github.com/akrabat/slim3-skeleton
Though other projects prob exist, search with github filters if still having issues.
Original Answer content
Please include more detail on the route and the other class, but here are 3 ways, with execution examples detailed further down.
This info does relate to Slim Framework 2, not the Slim 3 beta, but slim 3 beta shows similar example code and makes no mention of overhauling changes, and in fact links to the Slim 2 documentation: http://docs.slimframework.com/configuration/names-and-scopes/
$this->app->getContainer()->get('rdb');
// Recommended approach, can be used in any file loaded via route() or include()
$app = \Slim\Slim::getInstance();
Slim::getInstance();
App::config('filename');
Slim3 Beta has only one code example, which looks like this:
$app = new \Slim\App();
// which would by extension mean that this 'might' work too
$app = \Slim\App::getInstance();
// but be sure to try with slim2 naming just in case
$app = \Slim\Slim::getInstance()
Though obviously this doesnt fit outside of index.php, but is consistent with Slim2 doco showing GetInstance works.
Which one fits you?
I have multiple files that use these different approaches, though i cant say what fits best as too little context on how this external class fits in and what its composition is.
For example, my controllers (which are endpoints of most my routes) use the same approach, through a base class or just direct:
class ApiBaseController /// extends \BaseController
{
protected $app;
protected $data;
public function __construct()
{
$this->app = Slim\Slim::getInstance();
$this->data = array();
}
//...
}
class VideoApiController extends \ApiBaseController
{
// ...
public function embed($uid)
{
// trace($this->app->response->headers());
$vid = \R::findOne('videos'," uid = ? ",array($uid));
if(!empty($vid))
{
// embed logic
}else{
// see my baseclass
$this->app->render('api/404.html', array(), 404);
}
}
// ...
// Returns the video file, keeping actual location obscured
function video($uid)
{
require_once(APP_PATH.'helpers/player_helper.php');
$data = \R::findOne('videos'," uid = ? ",array($uid));
/// trace($_SERVER); die();
if($data)
{
stream_file($data['filename']);
}else{
$app = \Slim\Slim::getInstance();
$app->render('404.html');
}
/// NOTE - only same domain for direct /v/:uid call
header('Access-Control-Allow-Origin : '.$_SERVER['HTTP_HOST']);
// header('X-Frame-Options: SAMEORIGIN');
// Exit to be certain nothing else returned
exit();
}
//...
}
My helper files show code like this:
function get_permissions_options_list($context = null)
{
if(empty($context)) $context = 'user';
return App::config('permissions')[$context];
}
My middleware:
function checkAdminRoutePermissions($route)
{
$passed = runAdminRoutePermissionsCheck($route);
if($passed)
return true;
// App::notFound();
// App::halt(403, $route->getPattern());
if(!Sentry::check())
App::unauthorizedNoLogin();
else
App::unauthorized();
return false;
}
Thats example of how i access in the various files, though the code you shared already shows that you have used the recommended approach already
$app = \Slim\Slim::getInstance();
Though again, need more info to say for sure how your external file fits in, but if its at the end of a route or in an 'include()', then it should work.
You said your old approach didnt work though, but gave no info on what the actual result vs expected result was (error msg, ect), so if this doesnt work please update the OP.
This was a tough one. #mgansler answer was really helpful, but in his answer he passed a database connection, and not exactly $app inside the controller
Following the same idea it is possible to send $app though.
First inside your dependencies.php you need to grab the $app and throw it in a container to inject it to the Controller later.
$container['slim'] = function ($c) {
global $app;
return $app;
};
Then you got to inject it:
// Generic Controller
$container['App\Controllers\_Controller'] = function ($c) {
return new _Controller($c->get('slim'));
};
Now on your controller.php:
private $slim;
/**
* #param \Psr\Log\LoggerInterface $logger
* #param \App\DataAccess $dataaccess
* #param \App\$app $slim
*/
public function __construct(LoggerInterface $logger, _DataAccess $dataaccess, $slim)
{
$this->logger = $logger;
$this->dataaccess = $dataaccess;
$this->slim = $slim;
}
Now you just got call it like this:
$this->slim->doSomething();

Laravel dependancy injection for views

I'm trying to get the hang of dependancy injection and the IoC container in Laravel.
Currently I have a class full of static methods that I use in my views. E.g.
class Templatizer {
/**
* Generates a colored FontAwsome check or cross from a true/false argument
* #param boolean $bool
* #return string The HTML markup of the icon
*/
public static function boolicon($bool)
{
return $bool ? '<span class="fa fa-check text-success"></span>' : '<span class="fa fa-times text-danger"></span>';
}
}
I have composer autoload the class and in my view I can just go {{ Templatizer::boolicon($trueOrFalseValue) }}.
Clearly this is bad practice and I'd like to move away from using static methods. I presume the correct way is to inject an instance of Templatizer and use the methods something like {{ $templatizer->boolicon($v) }}`.
How would I structure this? Presumably I'd need to inject an instance of Templatizer into my controller via the constructor? e.g.
class PagesController extends BaseController {
protected $templatizer;
public function __construct(Templatizer $templatizer)
{
$this->templatizer = $templatizer;
}
}
And then for, say, the method for the index page I pass the instance to the view? e.g.
# inside PagesController
public function index()
{
return View::make('pages.index', ['templatizer' => $this->templatizer]);
}
If this is correct, where is an appropriate place to put my Templatizer class? How would I bind it to the IoC container?
First of all I don't see anything wrong with calling these methods statically. It looks like this is just a HTML helper class of your own and you should be fine.
If you decide to go with Dependency Injection the "correct" way to register your class would be using Service Providers.
Assigning it to the views the way you wrote would work but you can also get it once you need it this way:
$templatizer = App::make('Yournamespace\Templatizer');
And finally probably the best solution in your case would be building your own Facade.
Yes you should inject it via the controller constructor, the files can live anywhere you like as long as they are autoloaded in.
I like to create a folder in the root directory called src so that my composer.json file looks like:
"autoload": {
"classmap": [
....
],
"psr-4": {
"Foo\\": "src/"
}
}
Then you can have src/Templatizer.php which would look like:
<?php namespace Foo;
class Templatizer {
}
Now you just need a service provider to bind your instance of Templatizer (this basically makes Laravel aware of your class and allows you to inject it into your controllers) in src/FooServiceProvider.php
<?php namespace Foo;
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred
*
* #var boolean
*/
protected $defer = false;
/**
* Register the service provider
*/
public function register() {
$this->app->bind('Foo\Templatizer', function($app) {
return new Templatizer();
});
}
}
Don't forget to add Foo\FooServiceProvider to the providers array in app config and you should be all set to do...
public function __construct(Foo\Templatizer $templatizer) {
You are also create instance on BaseController.
class BaseController extends Controller {
/**
* Setup the layout used by the controller.
*
* #return void
*/
var $templatizer;
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$this->layout = View::make($this->layout);
$templatizer = new \Templatizer();
View::share('templatizer', $templatizer);
}
}
}
use this $templatizer instance in a all view.
something like {{ $templatizer->boolicon($v) }}`.

Laravel : Calling functions defined in base_controller from view

In using the laravel framework, how can I call a function defined in base_controller, in a view. For exacmple:
class Base_Controller extends Controller {
public static function format_something()
{
return something;
}
}
How can i call format_something() in a view file?
Usually the error I get looks something like this:
Method [link_to_action] is not defined on the View class.
Probably a silly question, but thanks in advance!
Edit
Okay! First the correct place to do something like this is in the libraries folder.
Second, problem is that your class cannot have underscores.
So in application/libraries I made file AppHelper.php with class
class AppHelper {
public static function format_something()
{
return something;
}
}
And can call it like:
$formated = AppHelper::format_something;
Thanks for the help and the good forum find Boofus McGoofus.
For me is working:
Create directory "helpers" or whatever and file:
// app/helpers/AppHelper.php
class AppHelper {
public static function format_something()
{
return something;
}
}
Add path to composer.json
// composer.json
"autoload": {
"classmap": [
"app/helpers" // <-------- add this line
]
},
Run: (reload the autoload)
composer dump-autoload
Now you can call:
$formated = AppHelper::format_something();
This answer was written for Laravel 3. For Laravel 4 and after, Lajdák Marek's answer using Composer's autoloader is better.
Functions like format_something() don't belong in the controller. The controller should just be about collecting data from various sources and passing it to the view. It's job is mostly just routing.
I've created a folder called "helpers" in the application folder for all my little helpery functions. To make sure all my controllers, views, and models have access to them, I've included the following in my start.php file:
foreach(glob(path('app').'helpers/*.php') as $filename) {
include $filename;
}
I suspect that there's a better way to do that, but so far it has worked for me.
You can inspire yourself from Laravel framework itself.
I will take your example of a formatter and refer to url helper in Laravel Framework.
Start by creating your own helpers.php file:
<?php
if (! function_exists('format_that')) {
/**
* Generate something
*
* #param string $text
* #return string
*/
function format_that($text)
{
return app('formatter')->format_that($text);
}
}
And add it to your composer.json file:
"autoload": {
"files": [
"app/helpers/helpers.php"
]
}
Run this command to recreate the autoload php file:
$ composer dumpautoload
Create your service provider app/Providers/FormatterServiceProvider.php:
<?php
namespace Illuminate\Routing;
use Illuminate\Support\ServiceProvider;
use App\Helpers\FormatGenerator;
class FormatterServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['formatter'] = $this->app->share(function ($app) {
return new FormatGenerator($app['request']);
});
}
}
Register your service provider. Laravel framework invokes register method but you only need to add it to your app config file config/app.php:
'providers' => [
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
// other providers...
App\Providers\FormatterServiceProvider::class,
]
Finally, create your actual generator class app/Helpers/FormatGenerator.php
<?php
namespace App\Helpers;
use Illuminate\Http\Request;
class FormatGenerator
{
protected $request;
/**
* Create a new URL Generator instance.
*
* #param \Illuminate\Routing\RouteCollection $routes
* #param \Illuminate\Http\Request $request
* #return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
public function format_that($text){
if ($request->path() == "home"){
return mb_strtoupper($text);
}
else{
return $text;
}
}
}
You can optionally create a Facade app/Facade/Formatter.php, to be able to do Formatter::format_that($text):
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
/**
* #see \App\Helpers\FormatGenerator
*/
class Formatter extends Facade
{
protected static function getFacadeAccessor() { return 'formatter'; }
}
You could ask yourself:
Why the facade? You can reuse the component somewhere else by simply calling Formatter::format_that($text) instead of app('formatter')->format_that($text). Sugar syntax really.
Why the Service provider? Dependence injections. If you need to use Request or want to build a complex object, the Service provider will take care of that for you and make it available in your $app object.

Categories