Get the calling class/method from a service provider in silex - php

If I had the following code how would I get the calling method and the class in the provider:
class HelloServiceProvider implements ServiceProviderInterface {
public function register(Application $app){
$app['hello'] = $app->share(function () {
// Get hello/indexAction
});
}
public function boot(Application $app){}
}
class hello {
public function addAction(){
$app['hello']()
}
}
$app->get('/hello', 'hello.controller:indexAction');
Is this even possible? Thanks

Is it, indeed, possible but you need some changes:
<?php
// file HelloServiceProvider.php
class HelloServiceProvider implements ServiceProviderInterface {
public function register(Application $app){
$app['hello'] = $app->share(function () {
// Get hello/indexAction
});
}
public function boot(Application $app){}
}
// file Hello.php
class Hello {
public function indexAction(Application $app){
$app['hello']()
}
}
// somewhere in your code:
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
$app->register(new HelloServiceProvider());
$app['hello.controller'] = $app->share(function() {
return new hello();
});
$app->get('/hello', 'hello.controller:indexAction');
Notice that the use statements are missing from code
You can get more information in the official documentation.

Related

how to create a unique key for caching query in laravel

I used repository in a project that caching all queries.
there's a BaseRepository.
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class BaseRepository implements BaseRepositoryInterface{
protected $model;
protected int $cacheDuration = 600; //per seconds
public function __construct(Model $model)
{
return $this->model = $model;
}
public function paginate(int $paginate,string $cacheKey)
{
return Cache::remember($cacheKey,$this->cacheDuration , function () use ($paginate) {
return $this->model->latest()->paginate($paginate);
});
}
// other methods ...
}
then i used this repository in my service
PostService:
use Illuminate\Support\Facades\App;
class PostService{
public PostRepositoryInterface $postRepository;
public function __construct()
{
$this->postRepository = App::make(PostRepositoryInterface::class);
}
public function paginate(int $paginate, string $cacheKey)
{
return $this->postRepository->paginate($paginate,$cacheKey);
}
}
finally i using the PostService in my controller
PostController:
class PostController extends Controller{
public PostService $postService;
public function __construct()
{
$this->postService = App::make(PostService::class);
}
public function index()
{
string $cacheKey = "posts.paginate";
return $this->postService->paginate(10);
}
}
the index method will return top 10 latest record correctly. now i need to create a unique CacheKey for all Repository queries. for example
TableName concat FunctionName // posts.paginate
so i can use this code into all method of Repository
public function paginate(int $paginate)
{
$cacheKey = $this->model->getTable().__FUNCTION__;
return Cache::remember($cacheKey,$this->cacheDuration , function () use ($paginate) {
return $this->model->latest()->paginate($paginate);
});
}
this is fine. but the problem is that this code repeat in all the method of this class.
if i use this code in another class, method name's will be incorrect.
What do you suggest to prevent duplication of this code?
I solve this problem by passing function name to another class
I created CacheKey class:
class CacheKey{
public static function generate(Model $model, $functionName):string
{
return $model->getTable()."_".$functionName;
}
}
Then in any method of repository we can use this helper class as follows:
$cacheKey = CacheKey::generate($this->model,__FUNCTION__);
you can easily use magic method in this way:
class CacheService {
private const $cacheableMethods = ['paginate'];
private $otherSerivce;
public __construct($otherSerivce) {
$this->otherSerivce = $otherSerivce;
}
public __get($method, $args) {
if(!in_array($method, static::$cachableMethods)) {
return $this->otherSerivce->{$method}(...$args);
}
return Cache::remember(implode([$method, ...$args], ':'), function () {
return $this->otherSerivce->{$method}(...$args);
});
}
}

Auto create logs (inserts data to table) whenever a function was called

I'm new to laravel and to php oop. My main goal is to call createLogs() everytime a function is called without putting the call method in each function because it's a hassle. I need help please.
I made a controller called WebLogs with a function called createLogs() that inserts data to a table. I want it to be auto-called whenever another function is called. I tried using this solution and put it in Controller class because WebLogs extends Controller class, and all my other controllers extends Controller class, but the solution doesn't seem to work.
So my Controller class now looks like this:
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
// Added this function from the solution I mentioned
public function __call($method, $arguments) {
echo 'hello world';
echo '<br><br>';
echo $method;
if(method_exists($this, $method)) {
return call_user_func_array(array($this,$method),$arguments);
}
}
}
Example controller:
class DashboardController extends Controller
{
public function index()
{
(new WebLogs)->createLogs(); //I don't want to call this for every function
return view('dashboard');
}
public function showSomething()
{
(new WebLogs)->createLogs();
return view('something');
}
public function updateSomething()
{
(new WebLogs)->createLogs();
return redirect()->back()->with('message','yeey');
}
}
How the functions from DashboardController are being called from web.php:
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
Route::get('/something', [DashboardController::class, 'showSomething'])->name('something');
Route::post('/something/update', [DashboardController::class, 'updateSomething'])->name('something.update');
Create app\Classes\WebLogs.php with content:
<?php
namespace App\Classes;
class WebLogs {
public function __construct() {
return "WebLogs class with construct function was initialized.";
}
public function createLogs($routeName,$routePath) {
$status = 0;
logger('WebLogs class is running:');
logger([$routeName,$routePath]);
// Save to database here
// ...
return $status;
}
}
Then, create an AutoCreateLogs middleware, it will save as app\Http\Middleware\AutoCreateLogs.php:
$ php artisan make:middleware AutoCreateLogs
With content:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Classes\WebLogs;
class AutoCreateLogs
{
public function handle(Request $request, Closure $next)
{
$route = Route::current();
$routePath = $route->uri;
$routeName = $route->action['as'];
$w = new WebLogs;
$w->createLogs($routeName,$routePath);
return $next($request);
}
}
And use this middleware like this:
Route::middleware([AutoCreateLogs::class])->group(function () {
Route::get('/', [App\Http\Controllers\DashboardController::class, 'index'])->name('dashboard');
Route::get('/something', [App\Http\Controllers\DashboardController::class, 'showSomething'])->name('something');
Route::post('/something/update', [App\Http\Controllers\DashboardController::class, 'updateSomething'])->name('something.update');
Route::get('/something/{value}', [App\Http\Controllers\DashboardController::class, 'getSomething'])->name('get.something');
});
With app\Http\Controllers\DashboardController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DashboardController extends Controller
{
public function index()
{
$page = 'index';
return view('welcome', ['page'=>$page]);
}
public function showSomething()
{
$page = 'showSomething';
return view('welcome', ['page'=>$page]);
}
public function updateSomething()
{
$page = 'updateSomething';
return response()->json(['page'=>$page]);
}
public function getSomething(Request $request)
{
$page = 'getSomething';
return view('welcome', ['page'=>$page]);
}
}
Then, empty storage\logs\laravel.log, and run with example route
http://laravel-me.com/something/value99
It will show the successful result:
[2022-02-18 22:56:09] local.DEBUG: WebLogs class is running:
[2022-02-18 22:56:09] local.DEBUG: array (
0 => 'get.something',
1 => 'something/{value}',
)
Read more about middleware: https://laravel.com/docs/8.x/middleware

How can I use $app in custom classes in Silex?

I know this looks like a dupllicate of Inject Silex $app in my custom class and others, but I couldn't get it working from their solutions.
I define my service like this:
$app['user.repo'] = function () {
return new MyApp\Repository\User();
};
My class looks like this:
<?php
namespace MyApp\Repository;
use Silex\Application;
class User {
public function findAll(Application $app) {
$users = $app['db']->fetchAll('SELECT * FROM user');
return $users;
}
}
And I use the service like this:
$users = $app['user.repo']->findAll($app);
How can I do this same thing without putting $app in all my methods?
Why don't you inject it?
$app['user.repo'] = function () use ($app) {
return new MyApp\Repository\User($app);
};
And here's your modified class:
<?php
namespace MyApp\Repository;
use Silex\Application;
class User {
/** #var Application */
protected $app;
public function __construct(Application $app) {
$this->app = $app;
}
public function findAll() {
$users = $app['db']->fetchAll('SELECT * FROM user');
return $users;
}
}
Or even better: instead of injecting the whole application (and thus hiding your real dependencies, making unit testing a pain), only inject what you really need:
$app['user.repo'] = function () use ($app) {
return new MyApp\Repository\User($app["db"]);
};
This way your class becomes:
<?php
namespace MyApp\Repository;
use Silex\Application;
class User {
protected $db;
public function __construct($db) {
$this->db = $db;
}
public function findAll() {
$users = $this->db->fetchAll('SELECT * FROM user');
return $users;
}
}

InvalidArgumentException in ControllerResolver in Silex

I'm newbie in working with framework and Silex,
I Trying to work with Silex and write my first project.
I use this silex-bootstrap : https://github.com/fbrandel/silex-boilerplate
and now in my app/app.php :
<?php
require __DIR__.'/bootstrap.php';
$app = new Silex\Application();
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
// Twig Extension
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
// Config Extension
$app->register(new Igorw\Silex\ConfigServiceProvider(__DIR__."/config/config.yml"));
$app->get('/admin', new App\Controller\Admin\AdminDashboard());
return $app;
and in app/Controller/Admin/AdminDashboard.php :
<?php
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ControllerCollection;
class AdminDashboard implements ControllerProviderInterface {
function __construct()
{
return "Dashboard";
}
function index()
{
return "Index Dashboard";
}
public function connect(Application $app)
{
return "OK";
}
}
When I trying to access to site I get this error:
http://localhost/project/public
InvalidArgumentException in ControllerResolver.php line 69:
Controller "App\Controller\Admin\AdminDashboard" for URI "/admin" is not callable.
What should I do ?
You're trying to use a controller provider as the actual controller. These are two different things. The provider simply registers controllers with your silex app. Your provider should look something like this:
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
class AdminDashboardProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
$controllers = $app['controllers_factory']();
$controllers->get('/', function() {
return 'Index Dashboard';
});
return $controllers;
}
}
Then you should mount that controller provider to your app in app/app.php.
$app->mount('/admin', new AdminDashboardProvider());
Obviously, this is not very elegant once you get more than a few controllers or if your controllers get big. That's where ServiceControllerServiceProvider comes in. It allows your controllers to be separate classes. I typically use a pattern like this:
<?php
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
class AdminDashboardProvider implements ControllerProviderInterface, ServiceProviderInterface
{
public function register(Application $app)
{
$app['controller.admin.index'] = $app->share(function () {
return new AdminIndexController();
});
}
public function connect(Application $app)
{
$controllers = $app['controllers_factory']();
$controllers->get('/', 'controller.admin.index:get');
return $controllers;
}
public function boot(Application $app)
{
$app->mount('/admin', $this);
}
}
The controller looks like this:
namespace App\Controller\Admin;
class AdminIndexController
{
public function get()
{
return 'Index Dashboard';
}
}
Then you can register it with your app in app/app.php like:
$app->register(new AdminDashboardProvider());

Problems with Facades in Laravel 4

Sorry for the English, but I am using the google translator.
First of all I leave my code:
FtpServiceProdiver.php
<?php namespace Jaimemse\Ftp;
use Illuminate\Support\ServiceProvider;
class FtpServiceProvider extends ServiceProvider {
protected $defer = false;
public function boot()
{
$this->package('jaimemse/ftp');
}
public function register()
{
$this->app->bind('ftp', function()
{
return new Ftp;
});
}
public function provides()
{
return array();
}
}
Ftp.php (the class)
<?php namespace Jaimemse\Ftp;
class Ftp {
public function hello()
{
return 'hola';
}
}
Facades/Ftp.php (Facade)
<?php namespace Jaimemse\Ftp\Facades;
use Illuminate\Support\Facades\Facade;
class Ftp extends Facade {
protected static function getFacadeAccessor() { return 'ftp'; }
}
app.php
'Jaimemse\Ftp\FtpServiceProvider',
'Ftp' => 'Jaimemse\Ftp\Facades\Ftp',
If instead of that Facade put this, if it works:
'Ftp' => 'Jaimemse\Ftp\Ftp',
The problem I have is that when using the alias in the file app.php seeks Ftp class in the folder Facades/Ftp.php
Call to undefined method Jaimemse\Ftp\Facades\Ftp::hello()
Someone can help me? Thanks!
You have to extend the BaseController:
<?php namespace Jaimemse\Ftp;
class Ftp extends \BaseController {
public function hello()
{
return 'hola';
}
}
Also your route should be (with namespace):
Route::get('/ftp', 'Jaimemse\Ftp\Ftp#hello');
Also
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
should be
use \Illuminate\Support\Facades\Facade;
use \Illuminate\Support\ServiceProvider;
You should put in app.php
'Jaimemse\Ftp\FtpServiceProvider', in 'providers' array (before 'aliases')
and in 'aliases' array
'Ftp' => 'Jaimemse\Ftp\Facades\Ftp',
I fixed it by adding in register method:
FtpServiceProvider.php
public function register()
{
$this->app->bind('ftp', function()
{
return new Ftp;
});
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Ftp', 'Jaimemse\Ftp\Ftp');
});
}
Ftp.php
class Ftp {
public function hello()
{
return 'hello';
}
}
App.php
'Jaimemse\Ftp\FtpServiceProvider',
I have not added any app.php alias in the file. I deleted Facade file.
Now I can do things like:
Ftp::hello();
Hope that helps someone. Thank you!

Categories