Laravel 5, check if class is registered in the container - laravel

Is there any way to check if a class exists in Laravel 5?
I had this solution for Laravel 4: try to make a specific class, and if I get a ReflectionException, I use a generic class.
In Laravel 5 looks like I can't intercept the ReflectionException and I get a "Whoops".
I was wondering if there is some better way to do this.
try {
$widgetObject = \App::make($widget_class);
} catch (ReflectionException $e) {
$widgetObject = \App::make('WidgetController');
$widgetObject->widget($widget);
}

Check whether your class is set in bindings by doing
app()->bound($classname);

Why don't you just use the PHP function class_exists?
if(class_exists($widget_class)){
// class exists
}

\App::bound() might be the proper way.
Latest laravel versions (Maybe >= 5.3, I don't know exactly) register service providers in alittle different way by default.
For example, a new way of registering:
$this->app->singleton(MyNamespace\MyClass::class, function()
{
/* do smth */ }
);
instead of the old one:
$this->app->singleton('MyClassOrAnyConvenientName', function()
{
/* do smth */ }
);
As a result we should use App::make('\MyNamespace\MyClass') instead of App::make('MyClassOrAnyConvenientName') to resolve a service.
We maintain a library which has to support both versions. So we use \App::bound() to determine whether old or new format of service name is registered in container. class_exists() did actually work for newer laravel but didn't work as expected for older ones because in old systems we didn't have a properly named Facade for that service (Facade's name differed from registered service name) and class_exists() returned false.

Use the getProvider method on the Application container:
if (app()->getProvider($classname)) {
// Do what you want when it exists.
}
It's been available since 5.0 and can be viewed here.

Related

Target class [Agent\App\Http\Controllers\PropertyController] does not exist when I put it inside the route group

I have this route in web.php
Route::group(['prefix'=>'agent','namespace'=>'Agent','middleware'=>
['auth','agent'],'as'=>'agent.'], function()
{
Route::get('/dashboard',[AgentController::class, 'index'])->name('dashboard');
Route::resource('/properties', PropertyController::class);
});
When I run the command below,
php artisan route:list
I got this error:
Illuminate\Contracts\Container\BindingResolutionException
Target class [Agent\App\Http\Controllers\PropertyController] does not
exist.
at
C:\xampp\htdocs\sweethomeFinal\vendor\laravel\framework\src\Illuminate\Container\Container.php:879
875▕
876▕ try {
877▕ $reflector = new ReflectionClass($concrete);
878▕ } catch (ReflectionException $e) {
879▕ throw new BindingResolutionException("Target class [$concrete] does
not exist.", 0, $e);
880▕ }
881▕
882▕ // If the type is not instantiable, the developer is attempting to resolve
883▕ // an abstract type such as an Interface or Abstract Class and
there is
1 [internal]:0
Illuminate\Foundation\Console\RouteListCommand::Illuminate\Foundation\Console{closure}(Object(Illuminate\Routing\Route))
2
C:\xampp\htdocs\sweethomeFinal\vendor\laravel\framework\src\Illuminate\Container\Container.php:877
ReflectionException::("Class Agent\App\Http\Controllers\PropertyController does not exist")
But when I put the "Route::resource('/properties', PropertyController::class);" outside the auth
Route::group(['prefix'=>'agent','namespace'=>'Agent','middleware'=>
['auth','agent'],'as'=>'agent.'], function()
{
Route::get('/dashboard',[AgentController::class, 'index'])->name('dashboard');
});
Route::resource('/properties', PropertyController::class);
It just shows all the route lists. But I wanted to put it inside the auth, may I know what is wrong?
Group namespaces made sense pre-Laravel 8, but now with the suggested way of defining routes as Controller::class the prefixes are basically useless.
Routing before Laravel 8
Before v8, Laravel used a default prefix defined in RouteServiceProvider of App\Http\Controllers\. This meant that you only needed to provide the last part - MyController and it was automatically built out to the fully qualified class name (App\Http\Controllers\MyController).
Routing beginning in v8
In v8, the default controller path was removed ($namespace = null), meaning you have to provide the fully qualified class name yourself, or add the prefix back to the service provider. The most efficient way to do this is using the ::class syntax which returns the required name. This method of providing the class name is also more IDE friendly, which was one of the main reasons for the switch.
The problem with route group namespaces
In the older way of building out the controller class name, the group namespaces were useful for sub folders in your controllers folder.
The path would get built out like:
{default_prefix} + {group_namespace} + {controller name}
Yielding:
App\Http\Controllers\ + Agent\ + PropertyController.
This is actually still how it works in version 8; however, you're providing the values in a different way:
(null) + Agent + App\Http\Controllers\PropertyController, which doesn't make the right path.
Summary
When using the ::class syntax for Laravel routes, group level namespace prefixes really don't make sense to use anymore.
If you browse the versions of the Laravel documentation, you'll also notice that the usage of group namespaces present in version 7 is not included in the version 8 docs, because even though it still "works", it's not very useful.
when you set namespace for a route group, all the routes in this group take that namespace as prefex for it's name.
remove ,'namespace'=>'Agent' form the group definition, it should solve it.
see laravel doc for more details.
your are using laravel 8. add the below line on your web.php .
use App\Http\Controllers\yourcontrollername;

Doctrine behavior *translation entity error: No identifier/primary key specified for Entity

After taking a src/bundle version of my bundle that makes use of the Knp translatable of the doctrine/behavior bundle. In the original bundle I everything works.
But when calling :
app/console doctrine:schema:validate
it would throw an error:
No identifier/primary key specified for Entity
Its deprecated way as said at knp behaviors documentation.
Add new Knp\DoctrineBehaviors\Bundle\DoctrineBehaviorsBundle(), to AppKernel.php
The problem seem to have been with the listeners not loading.
adding to config.yml
- { resource: ../../vendor/knplabs/doctrine-behaviors/config/orm-services.yml }
did the trick :)
Or the new way to modify AppKernel:
class AppKernel
{
function registerBundles()
{
$bundles = array(
//...
new Knp\DoctrineBehaviors\Bundle\DoctrineBehaviorsBundle(),
//...
);
//...
return $bundles;
}
}
You can also register them using doctrine2 api:
<?php
$em->getEventManager()->addEventSubscriber(new \Knp\DoctrineBehaviors\ORM\Translatable\TranslatableSubscriber);
// register more if needed
see: https://github.com/KnpLabs/DoctrineBehaviors#subscribers

Add Custom Variable to Laravel Error Log

I'd like to log the user's name along with the error that is outputted to the log. How do I add a variable to the beginning of an error log entry that outputs an exception?
I think I've got a fairly easy way to do this.
Solution 1
Create a new folder under app called handlers and create a new class called CustomStreamHandler.php which will hold the custom monolog handler.
namespace App\Handlers;
use Monolog\Handler\StreamHandler;
use Auth;
class CustomStreamHandler extends StreamHandler
{
protected function write(array $record)
{
$record['context']['user'] = Auth::check() ? Auth::user()->name : 'guest';
parent::write($record);
}
}
Make sure you set the namespace if you changed it from App and also modify the line where it's setting the user in the context so it works with your users table.
Now we need to drop the current StreamHandler from monolog. Laravel sets this up by default and as far as I can see, there isn't a good way to stop Laravel from doing this.
in app/Providers/AppServiceProvider, we should modify the boot() function to do remove the handler and insert the new one. Add the following...
// Get the underlying instance of monolog
$monolog = \Log::getMonolog();
// Instantiate a new handler.
$customStreamHandler = new \App\Handlers\CustomStreamHandler(storage_path('logs/laravel.log'));
// Set the handlers on monolog. Note this would remove all existing handlers.
$monolog->setHandlers([$customStreamHandler]);
Solution 2
This is a much easier solution but also not exactly what you are looking for (but it might still work for you).
Add the following to AppServiceProvider.php boot().
Log::listen(function()
{
Log::debug('Additional info', ['user' => Auth::check() ? Auth::user()->name : 'guest']);
});
This will simply listen for any logging and also log a debug line containing user information.

Zend framework 2 : Add different authentication adapter for two different modules

I have two different modules. Now I need to add different authentication mechanism for both modules.
So I added event code first module's Module.php's onBootstrap method
$listener = $serviceManager->get('First\Service\AuthListener');
$listener->setAdapter($serviceManager->get('First\Service\BasicAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
and in second module's Module.php's onBootstrap method
$listener = $serviceManager->get('Second\Service\AuthListener');
$listener->setAdapter($serviceManager->get('Second\Service\AdvAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
Now if I disable one of modules, functionality working fine and request properly authenticated. While enabling both module do some kind of overlapping So even required module properly authenticated, But other module event code also got executed and system give not authenticated error.
I am thinking this due to event handler code in both module.php is executed without take care of requested module url.
I can verify with requested route pattern before authentication, But that is look like a hack instead of good solution.
If better solution exists for handling this issue ?
UPDATE :
My AuthListener Code :
namespace First\Service;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Mvc\MvcEvent;
class AuthListener
{
protected $adapter;
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function __invoke(MvcEvent $event)
{
$result = $this->adapter->authenticate();
if (!$result->isValid()) {
$response = $event->getResponse();
// Set some response content
$response->setStatusCode(401);
$routeMatch = $event->getRouteMatch();
$routeMatch->setParam('controller', 'First\Controller\Error');
$routeMatch->setParam('action', 'Auth');
}
}
}
There is a good way to make module specific bootstrap - to use SharedManager:
$e->getApplication()->getEventManager()->getSharedManager()
->attach(__NAMESPACE__, 'dispatch', function(MvcEvent $e) {
// This code will be executed for all controllers in current __NAMESPACE__
}, 100);
Here is a good article to understand difference between EventManager and SharedEventManager
There is no additional info about listeners in the question, but I try to guess:
If you use as listener some callable class - it's ok, just replace function() { } by your $listener.
If you use as listener some class, that implements
ListenerAggregateInterface, you should convert listeners to
SharedListenerAggregateInterface and use method attachAggregate
instead of attach
I hope it helps!

how to integrate SMF with CodeIgniter?

I am working on a project that will solely use the SMF member system. Meaning that SMF is the only member system that I am using to have logins, not my own system along with SMF, just plain SMF. However, I am running into one problem... I need to be able to get information from SMF based on a given ID_MEMBER. My article database uses SMF's ID_MEMBER field as the author information. Seeing that CodeIgniter is MVC and uses classes, I searched for a general SMF class all over the internet, but could not find one. I decided to write my own, but cannot get it to work correctly... I don't have much experience with OOP.
<?php
class smf {
var $context;
var $memberContext;
function __construct($who){
include('/../../forums/SSI.php');
$this->context = $context;
}
function user_context(){
return $this->context['user'];
}
function member_context($id = NULL){
loadMemberData($id);
loadMemberContext($id);
return $memberContext[$id];
}
}
The user_context function works fine, but not the member_context function. In the SSI.php file, the LoadmemberContext function compiles all of the information into an array called $memberContext[$id]. When I call the method from my CodeIgniter controller, I get an error saying that the $memberContext variable is undefined. Why is this happening?
I stumbled upon this question too and managed to fix it. Try this, it worked for me:
function member_context($id = NULL) {
loadMemberData($id);
loadMemberContext($id);
global $user_profile;
return $user_profile[$id];
}

Resources