Laravel Nova - Change Tool to Resource Tool - laravel

I'm trying to convert a build Nova tool to a resource tool. I've tried to change my Tool to extend ResourceTool instead of Tool and add the resource tool to the Resource in the fields, but it's not showing up. I also changed the code of ToolServiceProvider to match that of a ResourceTool but it does not work unfortunately.
I've searched the internet but I seem to be the only one having this issue, anyone ever experienced this and know what to do? I'm not getting any error message, the resource tool just is not showing up.
Here is my code, I removed some of it for readability and confidentiality.
Product (Resource)
<?php
namespace App\Nova;
use confidentiality\SalesHistory\SalesHistory;
class Product extends Resource
{
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->sortable(),
SalesHistory::make(),
];
}
}
SalesHistory (Resource tool)
<?php
namespace confidentiality\SalesHistory;
use Laravel\Nova\ResourceTool;
class SalesHistory extends ResourceTool
{
/**
* Get the displayable name of the resource tool.
*
* #return string
*/
public function name()
{
return 'Sales History';
}
/**
* Get the component name for the resource tool.
*
* #return string
*/
public function component()
{
return 'sales-history';
}
}
ToolServiceProvider (Resource Tool)
<?php
namespace confidentiality\SalesHistory;
use Laravel\Nova\Nova;
use Laravel\Nova\Events\ServingNova;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class ToolServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$this->app->booted(function () {
$this->routes();
});
Nova::serving(function (ServingNova $event) {
Nova::script('sales-history', __DIR__.'/../dist/js/tool.js');
Nova::style('sales-history', __DIR__.'/../dist/css/tool.css');
});
}
/**
* Register the tool's routes.
*
* #return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::middleware(['nova'])
->prefix('nova-vendor/sales-history')
->group(__DIR__.'/../routes/api.php');
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}

I managed to fix it finally. I also had to change the tool.js file to match that of a Resource Tool.
Nova.booting((Vue, router, store) => {
Vue.component('sales-history', require('./components/Tool'))
})

Try this command from the root of project: composer update

Related

Laravel 8 Fortify - 2FA only when the user logs in from a new device

I am implementing two-factor authentication (2FA) in my Laravel 8 application.
The 2FA is applied every time the user logs in. However, I don't really feel that 2FA is necessary every time, I even find it annoying. As a solution I am thinking of applying it only when the user connects from a new device. Is there someone who has already done it or who can give me a hint of the changes that would be necessary?
I have got it. Here are the steps I have followed:
In the config file fortify.php I have added
'pipelines' => [
'login' => [
App\Actions\Fortify\RedirectIfTwoFactorAuthenticatable::class,
Laravel\Fortify\Actions\AttemptToAuthenticate::class,
Laravel\Fortify\Actions\PrepareAuthenticatedSession::class,
]
]
I have added the field two_factor_cookies to the User class.
I have customized the RedirectIfTwoFactorAuthenticatable class of
Fortify:
<?php
namespace App\Actions\Fortify;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable as DefaultRedirectIfTwoFactorAuthenticatable;
use Laravel\Fortify\TwoFactorAuthenticatable;
class RedirectIfTwoFactorAuthenticatable extends DefaultRedirectIfTwoFactorAuthenticatable
{
/**
* Handle the incoming request.
*
* #param \Illuminate\Http\Request $request
* #param callable $next
* #return mixed
*/
public function handle($request, $next)
{
$user = $this->validateCredentials($request);
if (optional($user)->two_factor_secret &&
in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user)) &&
$this->checkIfUserDeviceHasNotCookie($user)) {
return $this->twoFactorChallengeResponse($request, $user);
}
return $next($request);
}
/**
* This checks if the user's device has the cookie stored
* in the database.
*
* #param \App\Models\User\User $user
* #return bool
*/
protected function checkIfUserDeviceHasNotCookie($user)
{
$two_factor_cookies = json_decode($user->two_factor_cookies);
if (!is_array($two_factor_cookies)){
$two_factor_cookies = [];
}
$two_factor_cookie = \Cookie::get('2fa');
return !in_array($two_factor_cookie,$two_factor_cookies);
}
}
In the FortifyServiceProvider I have added a customized TwoFactorLoginResponse.
<?php
namespace App\Providers;
use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Http\Responses\FailedPasswordResetLinkRequestResponse;
use App\Http\Responses\FailedPasswordResetResponse;
use App\Http\Responses\LockoutResponse;
use App\Http\Responses\LoginResponse;
use App\Http\Responses\LogoutResponse;
use App\Http\Responses\PasswordResetResponse;
use App\Http\Responses\RegisterResponse;
use App\Http\Responses\SuccessfulPasswordResetLinkRequestResponse;
use App\Http\Responses\TwoFactorLoginResponse;
use App\Http\Responses\VerifyEmail;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse as FailedPasswordResetLinkRequestResponseContract;
use Laravel\Fortify\Contracts\FailedPasswordResetResponse as FailedPasswordResetResponseContract;
use Laravel\Fortify\Contracts\LockoutResponse as LockoutResponseContract;
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
use Laravel\Fortify\Contracts\LogoutResponse as LogoutResponseContract;
use Laravel\Fortify\Contracts\PasswordResetResponse as PasswordResetResponseContract;
use Laravel\Fortify\Contracts\RegisterResponse as RegisterResponseContract;
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse as SuccessfulPasswordResetLinkRequestResponseContract;
use Laravel\Fortify\Contracts\TwoFactorLoginResponse as TwoFactorLoginResponseContract;
use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->registerResponseBindings();
}
/**
* Register the response bindings.
*
* #return void
*/
protected function registerResponseBindings()
{
$this->app->singleton(LoginResponseContract::class, LoginResponse::class);
$this->app->singleton(LogoutResponseContract::class, LogoutResponse::class);
$this->app->singleton(TwoFactorLoginResponseContract::class, TwoFactorLoginResponse::class);
$this->app->singleton(RegisterResponseContract::class, RegisterResponse::class);
$this->app->singleton(LockoutResponseContract::class, LockoutResponse::class);
$this->app->singleton(SuccessfulPasswordResetLinkRequestResponseContract::class, SuccessfulPasswordResetLinkRequestResponse::class);
$this->app->singleton(FailedPasswordResetLinkRequestResponseContract::class, FailedPasswordResetLinkRequestResponse::class);
$this->app->singleton(PasswordResetResponseContract::class, PasswordResetResponse::class);
$this->app->singleton(FailedPasswordResetResponseContract::class, FailedPasswordResetResponse::class);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Fortify::ignoreRoutes();
Fortify::loginView(function () {
return view('auth.login');
});
Fortify::twoFactorChallengeView('auth.two-factor-challenge');
Fortify::confirmPasswordView(function (Request $request) {
if ($request->ajax()) {
return view('auth.confirm-password-form');
} else {
return view('auth.confirm-password');
}
});
Fortify::requestPasswordResetLinkView(function () {
return view('auth.forgot-password');
});
Fortify::resetPasswordView(function ($request) {
return view('auth.reset-password', ['request' => $request,'token' => $request->route('token')]);
});
Fortify::registerView(function () {
return view('auth.register');
});
Fortify::verifyEmailView(function () {
return view('auth.verify');
});
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
/*RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->email.$request->ip());
});*/
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
}
}
Finally, the TwoFactorLoginResponse:
<?php
namespace App\Http\Responses;
use Illuminate\Http\JsonResponse;
use Laravel\Fortify\Contracts\TwoFactorLoginResponse as TwoFactorLoginResponseContract;
class TwoFactorLoginResponse implements TwoFactorLoginResponseContract
{
/**
* Create an HTTP response that represents the object.
*
* #param \Illuminate\Http\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function toResponse($request)
{
$user = \Auth::user();
$this->storeCookieIfNotInDB($user);
$role = $user->role;
if ($request->wantsJson()) {
return new JsonResponse('', 204);
}
if ($role == "0") {
return redirect()->route('user.home');
} else {
return redirect()->route('admin.home');
}
}
/**
* Store the cookie if it is not in the database.
*
* #param \App\Models\User\User $user
* #return void
*/
protected function storeCookieIfNotInDB($user)
{
$two_factor_cookies = json_decode($user->two_factor_cookies);
if (!is_array($two_factor_cookies)){
$two_factor_cookies = [];
}
$two_factor_cookie = \Cookie::get('2fa');
if (!in_array($two_factor_cookie,$two_factor_cookies)) {
$two_factor_cookie = md5(now());
$two_factor_cookies[] = $two_factor_cookie;
if (count($two_factor_cookies) > 3) {
array_shift($two_factor_cookies);
}
$user->two_factor_cookies = json_encode($two_factor_cookies);
$user->save();
$lifetime = 60 * 24 * 365; //one year
\Cookie::queue('2fa',$two_factor_cookie,$lifetime);
}
}
}
Upon login, it will look for the cookie 2fa. If its content is stored in the database, it will not be necessary to enter the code again. To prevent unlimited cookie content from being saved in the DB you can add a maximum limit (I have set it 3).
Thanks to Maarten Veerman for the inital help.
According to this line: https://github.com/laravel/fortify/blob/82c99b6999f7e89f402cfd7eb4074e619382b3b7/src/Http/Controllers/AuthenticatedSessionController.php#L80
you can create a pipelines.login entry in your fortify config file.
The solution would be to:
create the config entry
copy the pipeline setup in the above file, line 84.
create a custom AttemptToAuthenticate class, make sure the pipeline config points to your new class.
make the new class extend the default fortify AttemptToAuthenticate class.
overwrite the handle function, add your logic in the new function, where you check for a cookie on the device.

Laravel 5.2 RethinkDB Service Provider Preventing Creation of Migration Files

I followed the installation to install this service provider, but each time I try to create a migration file I get this error:
[ErrorException]
Argument 2 passed to duxet\Rethinkdb\Console\Migrations\MigrateMakeCommand::__construct() must be an instance
of Illuminate\Foundation\Composer, instance of Illuminate\Support\Composer given, called in D:\projects\app\vendor\duxet\laravel-rethinkdb\src\RethinkdbServiceProvider.php on line 41 and defined
I'm on line 41 and can see that the second parameter is $composer, which equals $composer = $app['composer'];. I thought maybe changing Illuminate\Support\Composer to Illuminate\Foundation\ServiceProvider might just fix the issue, but doing this throws another error saying that:
[Symfony\Component\Debug\Exception\FatalErrorException]
Class 'Illuminate\Foundation\ServiceProvider' not found
Anyone running into this issue?
Update
Where the original error is occurring (I marked Line 41):
<?php
namespace duxet\Rethinkdb;
use duxet\Rethinkdb\Console\Migrations\MigrateMakeCommand;
use duxet\Rethinkdb\Eloquent\Model;
use duxet\Rethinkdb\Migrations\MigrationCreator;
use Illuminate\Support\ServiceProvider;
class RethinkdbServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot()
{
Model::setConnectionResolver($this->app['db']);
Model::setEventDispatcher($this->app['events']);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->resolving('db', function ($db) {
$db->extend('rethinkdb', function ($config) {
return new Connection($config);
});
});
$this->app->singleton('command.rethink-migrate.make', function ($app) {
$creator = new MigrationCreator($app['files']);
$composer = $app['composer'];
return new MigrateMakeCommand($creator, $composer); <= line 41
});
$this->commands('command.rethink-migrate.make');
}
public function provides()
{
return ['command.rethink-migrate.make'];
}
}
The MigrateMakeCommand class:
<?php
namespace duxet\Rethinkdb\Console\Migrations;
use duxet\Rethinkdb\Migrations\MigrationCreator;
use Illuminate\Database\Console\Migrations\MigrateMakeCommand as LaravelMigration;
use Illuminate\Foundation\Composer;
class MigrateMakeCommand extends LaravelMigration
{
/**
* The console command signature.
*
* #var string
*/
protected $signature = 'make:rethink-migration {name : The name of the migration.}
{--create= : The table to be created.}
{--table= : The table to migrate.}
{--path= : The location where the migration file should be created.}';
/**
* Create a new migration install command instance.
*
* #param duxet\Rethinkdb\Migrations\MigrationCreator $creator
* #param \Illuminate\Foundation\Composer $composer
*
* #return void
*/
public function __construct(MigrationCreator $creator, Composer $composer)
{
parent::__construct($creator, $composer);
}
}

Laravel 5 Command and Handler issue

I am working one of my project with laravel 5. During the implementation i got struct with one issue which is related to command and handler.
I used artisan command to generate command
php artisan make:command TestCommand --handler
I generated command at app/commands folder "TestCommand.php"
<?php
namespace App\Commands;
use App\Commands\Command;
class TestCommand extends Command
{
public $id;
public $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}
Also my TestCommandHandler.php looks like this
<?php
namespace App\Handlers\Commands;
use App\Commands\TestCommand;
use Illuminate\Queue\InteractsWithQueue;
class TestCommandHandler
{
/**
* Create the command handler.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the command.
*
* #param TestCommand $command
* #return void
*/
public function handle(TestCommand $command)
{
dd($command);
}
}
Whenever dispatch this command from controller it shows following issue
InvalidArgumentException in Dispatcher.php line 335:
No handler registered for command [App\Commands\TestCommand]
Please, Anybody help me to solve this problem. Thank you
By default Laravel 5.1.x does not included BusServiceProvider. So we should create BusServiceProvider.php under provider folder and include that in to config/app.php.
BusServiceProvider.php
<?php namespace App\Providers;
use Illuminate\Bus\Dispatcher;
use Illuminate\Support\ServiceProvider;
class BusServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #param \Illuminate\Bus\Dispatcher $dispatcher
* #return void
*/
public function boot(Dispatcher $dispatcher)
{
$dispatcher->mapUsing(function($command)
{
return Dispatcher::simpleMapping(
$command, 'App\Commands', 'App\Handlers\Commands'
);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
config/app.php
'providers' => [
App\Providers\BusServiceProvider::class
]
So it may help others. Thank you

laravel 5 model class not found

I’m new to Laravel 5. I tried to create Customer model in my Models folder. I created both CustomerController and Customer via php artisan make:... and now I get the error:
FatalErrorException in CustomerController.php line 30: Class 'App\Http\Controllers\Cusomter' not found.
My Customer model:
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model {
//
protected $table = 'Customer';
protected $primaryKey = 'CID';
// protected $fillable = [
// 'Name',
// 'Gender',
// 'Address',
// 'DiscountPercent',
// 'Email',
// 'Phone'
// ];
}
My CustomerController:
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Customer;
class CustomerController extends Controller {
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//
$customers = Customer::all();
return View::make('Customer.home',compact('customers'));
// return View('Customer.home');
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
I dont know what i was doing anything wrong. Is there any help. I also did some search on Internet and i know its about namespace. Is there any ebook or documents for me to understand how namespace works, how to use and do i need to declare before using it
You’ve spelt “Customer” wrong in your index action.
You should add autoload classmap in composer.json
"autoload": {
"classmap": [
"database",
"app/Models"
],
"psr-4": {
"App\\": "app/"
}
},
update composer

Specify a different public path

My Laravel app works in the private folder and I need to tell Laravel that the public path is different.
Today I have upgraded a Laravel app from 4.2 to 5.0 and I can't find where we specify the public path since the paths.php file doesn't exist anymore in Laravel 5.0.
In laravel 4.2 we had the /bootstrap/paths.php file:
/*
|--------------------------------------------------------------------------
| Public Path
|--------------------------------------------------------------------------
|
| The public path contains the assets for your web application, such as
| your JavaScript and CSS files, and also contains the primary entry
| point for web requests into these applications from the outside.
|
*/
'public' => __DIR__.'/../../../public_html',
I'm not used with the Laravel 5.0 folders structure yet, any help would be greatly appreciated.
What worked for me flawlessly was adding to public/index.php the following three lines:
$app->bind('path.public', function() {
return __DIR__;
});
This was answered at Laracast.
I think that this could be made in different ways, here is mine.
Create a new helper file. You can create it at Services folder:
# app/Services/helper.php
if ( ! function_exists('private_path')){
function private_path($path = ''){
return app_path() . 'private/'
}
}
A good place to import the helper file is in AppServiceProvider that resides at app/Providers/AppServiceProvider.php. Use the boot to do so.
public function boot()
{
include __dir__ . "/../Services/helper.php";
}
Rename the folder from public to private, and finally you can call your own function from anywhere as:
$path = private_path();
According to this post, to replace the original public path we need to override the application paths:
<?php namespace App;
use Illuminate\Foundation\Application;
class MyApp extends Application {
protected $appPaths = array();
/**
* Create a new Illuminate application instance.
*
* #param array|null $appPaths
* #return \MyApp
*/
public function __construct($appPaths = null)
{
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
if (!is_array($appPaths)) {
abort(500, '_construct requires paths array');
}
if (!isset($appPaths['base'])) {
abort(500, '_construct requires base path');
}
$this->appPaths = $appPaths;
$this->setBasePath($appPaths['base']);
}
/**
* Set the base path for the application.
*
* #param string $basePath
* #return $this
*/
public function setBasePath($basePath)
{
$this->basePath = $basePath;
$this->bindPathsInContainer();
return $this;
}
/**
* Bind all of the application paths in the container.
*
* #return void
*/
protected function bindPathsInContainer()
{
$this->instance('path', $this->path());
foreach (['base', 'config', 'database', 'lang', 'public', 'storage'] as $path)
{
$this->instance('path.'.$path, $this->{$path.'Path'}());
}
}
/**
* Get the path to the application "app" directory.
*
* #return string
*/
public function path()
{
return $this->basePath.'/app';
}
/**
* Get the base path of the Laravel installation.
*
* #return string
*/
public function basePath()
{
return $this->basePath;
}
/**
* Get the path to the application configuration files.
*
* #return string
*/
public function configPath()
{
if (isset($this->appPaths['config'])) {
return $this->appPaths['config'];
}
return $this->basePath.'/config';
}
/**
* Get the path to the database directory.
*
* #return string
*/
public function databasePath()
{
if (isset($this->appPaths['database'])) {
return $this->appPaths['database'];
}
return $this->basePath.'/database';
}
/**
* Get the path to the language files.
*
* #return string
*/
public function langPath()
{
if (isset($this->appPaths['lang'])) {
return $this->appPaths['lang'];
}
return $this->basePath.'/resources/lang';
}
/**
* Get the path to the public / web directory.
*
* #return string
*/
public function publicPath()
{
if (isset($this->appPaths['public'])) {
return $this->appPaths['public'];
}
return $this->basePath.'/public';
}
/**
* Get the path to the storage directory.
*
* #return string
*/
public function storagePath()
{
if (isset($this->appPaths['storage'])) {
return $this->appPaths['storage'];
}
return $this->basePath.'/storage';
}
}
This seems weird for me and as it's been mentioned in the post, it feels like we did a step back in Laravel's functionalities, I hope they will change it in a future update.

Resources