Skip Global Middleware for specific Rout in Laravel 5.3 - laravel-5.3

I created one global Middleware and it works perfectly fine for all the Routs but now i need to skip one Rout from that Middleware Following is My MIddleware and Routs and Kernal.php file
MIddleware:Check shop
<?php
namespace App\Http\Middleware;
use Closure;
class Checkshop
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(session('shop'))
{
$shop = session('shop');
}
else{
if($request['shop'])
{
session(['shop' => $request['shop']]);
$shop = session('shop');
}
else{
dd('session expiere');
}
}
return $next($request);
}
}
Kernal.php
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* #var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
];
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\Checkshop::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'cors' => \App\Http\Middleware\Cors::class,
//'checkshop' => \App\Http\Middleware\Checkshop::class,
];
}
Web.php
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('callback', 'callbackController#index')->name('callback');
Route::get('redirect', 'callbackController#redirect')->name('redirect');
Route::get('dashboard', 'dateController#index')->name('dashboard');
Route::any('order','orderController#index')->name('order');
Route::post('editorder/{id}', 'orderController#update')->name('edit');
Route::post('saveconfig','dateController#store');
Route::get('getconfig','dateController#selectdate')->middleware('cors')->name('getconfig');
Route::any('edit/{id}','dateController#update')->name('edit');
Route::get('uninstall', 'callbackController#uninstall')->name('uninstall');
Route::get('donwload-snippet', 'callbackController#download_snippet')->name('donwload-snippet');
I want to skip the getconfig Route in the web.php file from applying the Checkshop MIddleware.

I recently bumped into the same kind of issue. You could try handling this in your Middleware itself rather trying to handle it in the controller level.
This may not be the best solution, but I did this.
below example, the Middleware stays away from "subscriptions" and "programs" urls.
namespace App\Http\Middleware;
use Closure;
class SampleMiddleware {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
protected $except_urls = [
'subscription',
'programs'
];
public function handle($request, Closure $next) {
$regex = '#' . implode('|', $this->except_urls) . '#';
if (preg_match($regex, $request->path()))
{
return $next($request);
}
// Rest of your proceedings
}
}

Related

FOSHttpCacheBundle cache invalidation with Symfony built-in reverse proxy doesn't work

I'm trying to do a hard thing: implementing cache invalidation with Symfony 4.4.13 using FOSHttpCacheBundle 2.9.0 and built-in Symfony reverse proxy.
Unfortunately, I can't use other caching solution (like Varnish or Nginx) because my hosting service doesn't offer them. So, the Symfony built-in reverse proxy is the only solution I have.
I've installed and configured FOSHttpCacheBundle (following the documentation). Also created a CacheKernel class and modified Kernel to use it (following Symfony official documentation, FOSHttpCache documentation and FOSHttpCacheBundle documentation).
After few tests (with my browser), the HTTP caching works and GET responses are cached (seen in browser network analyzer). But, when I update a resource with PUT/PATCH/POST, the GET responses still come from the cache and are unchanged until the expiration. My deduction is the invalidation doesn't work.
Have I do something wrong? Can you help me to troubleshoot?
See my code and configuration below.
config/packages/fos_http_cache.yaml
fos_http_cache:
cache_control:
rules:
-
match:
path: ^/
headers:
cache_control:
public: true
max_age: 15
s_maxage: 30
etag: "strong"
cache_manager:
enabled: true
invalidation:
enabled: true
proxy_client:
symfony:
tags_header: My-Cache-Tags
tags_method: TAGPURGE
header_length: 1234
purge_method: PURGE
use_kernel_dispatcher: true
src/CacheKernel.php
<?php
namespace App;
use FOS\HttpCache\SymfonyCache\CacheInvalidation;
use FOS\HttpCache\SymfonyCache\CustomTtlListener;
use FOS\HttpCache\SymfonyCache\DebugListener;
use FOS\HttpCache\SymfonyCache\EventDispatchingHttpCache;
use FOS\HttpCache\SymfonyCache\PurgeListener;
use FOS\HttpCache\SymfonyCache\RefreshListener;
use FOS\HttpCache\SymfonyCache\UserContextListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
use Symfony\Component\HttpKernel\HttpCache\Store;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class CacheKernel extends HttpCache implements CacheInvalidation
{
use EventDispatchingHttpCache;
// Overwrite constructor to register event listeners for FOSHttpCache.
public function __construct(HttpKernelInterface $kernel, SurrogateInterface $surrogate = null, array $options = [])
{
parent::__construct($kernel, new Store($kernel->getCacheDir()), $surrogate, $options);
$this->addSubscriber(new CustomTtlListener());
$this->addSubscriber(new PurgeListener());
$this->addSubscriber(new RefreshListener());
$this->addSubscriber(new UserContextListener());
if (isset($options['debug']) && $options['debug'])
$this->addSubscriber(new DebugListener());
}
// Made public to allow event listeners to do refresh operations.
public function fetch(Request $request, $catch = false)
{
return parent::fetch($request, $catch);
}
}
src/Kernel.php
<?php
namespace App;
use FOS\HttpCache\SymfonyCache\HttpCacheAware;
use FOS\HttpCache\SymfonyCache\HttpCacheProvider;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
class Kernel extends BaseKernel implements HttpCacheProvider
{
use MicroKernelTrait;
use HttpCacheAware;
private const CONFIG_EXTS = '.{php,xml,yaml,yml}';
public function __construct(string $environment, bool $debug)
{
parent::__construct($environment, $debug);
$this->setHttpCache(new CacheKernel($this));
}
...
public/index.php
<?php
use App\Kernel;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;
require dirname(__DIR__).'/config/bootstrap.php';
...
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$kernel = $kernel->getHttpCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
One of mine controller, src/Controller/SectionController.php (NOTE: routes are defined in YAML files)
<?php
namespace App\Controller;
use App\Entity\Section;
use App\Entity\SectionCollection;
use App\Form\SectionType;
use FOS\HttpCacheBundle\Configuration\InvalidateRoute;
use FOS\RestBundle\Controller\AbstractFOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class SectionController extends AbstractFOSRestController
{
/**
* List all sections.
*
* #Rest\View
* #param Request $request the request object
* #return array
*
* Route: get_sections
*/
public function getSectionsAction(Request $request)
{
return new SectionCollection($this->getDoctrine()->getRepository(Section::class)->findAll());
}
/**
* Get a single section.
*
* #Rest\View
* #param Request $request the request object
* #param int $id the section id
* #return array
* #throws NotFoundHttpException when section not exist
*
* Route: get_section
*/
public function getSectionAction(Request $request, $id)
{
if (!$section = $this->getDoctrine()->getRepository(Section::class)->find($id))
throw $this->createNotFoundException('Section does not exist.');
return array('section' => $section);
}
/**
* Get friends of the section's user.
*
* #Rest\View
* #return array
*
* Route: get_friendlysections
*/
public function getFriendlysectionsAction()
{
return $this->get('security.token_storage')->getToken()->getUser()->getSection()->getMyFriends();
}
private function processForm(Request $request, Section $section)
{
$em = $this->getDoctrine()->getManager();
$statusCode = $em->contains($section) ? Response::HTTP_NO_CONTENT : Response::HTTP_CREATED;
$form = $this->createForm(SectionType::class, $section, array('method' => $request->getMethod()));
// If PATCH method, don't clear missing data.
$form->submit($request->request->get($form->getName()), $request->getMethod() === 'PATCH' ? false : true);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($section);
$em->flush();
$response = new Response();
$response->setStatusCode($statusCode);
// set the 'Location' header only when creating new resources
if ($statusCode === Response::HTTP_CREATED) {
$response->headers->set('Location',
$this->generateUrl(
'get_section', array('id' => $section->getId()),
true // absolute
)
);
}
return $response;
}
return View::create($form, Response::HTTP_BAD_REQUEST);
}
/**
*
* Creates a new section from the submitted data.
*
* #Rest\View
* #return FormTypeInterface[]
*
* #InvalidateRoute("get_friendlysections")
* #InvalidateRoute("get_sections")
*
* Route: post_section
*/
public function postSectionsAction(Request $request)
{
return $this->processForm($request, new Section());
}
/**
* Update existing section from the submitted data.
*
* #Rest\View
* #param int $id the section id
* #return FormTypeInterface[]
* #throws NotFoundHttpException when section not exist
*
* #InvalidateRoute("get_friendlysections")
* #InvalidateRoute("get_sections")
* #InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: put_section
*/
public function putSectionsAction(Request $request, $id)
{
if (!$section = $this->getDoctrine()->getRepository(Section::class)->find($id))
throw $this->createNotFoundException('Section does not exist.');
return $this->processForm($request, $section);
}
/**
* Partially update existing section from the submitted data.
*
* #Rest\View
* #param int $id the section id
* #return FormTypeInterface[]
* #throws NotFoundHttpException when section not exist
*
* #InvalidateRoute("get_friendlysections")
* #InvalidateRoute("get_sections")
* #InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: patch_section
*/
public function patchSectionsAction(Request $request, $id)
{
return $this->putSectionsAction($request, $id);
}
/**
* Remove a section.
*
* #Rest\View(statusCode=204)
* #param int $id the section id
* #return View
*
* #InvalidateRoute("get_friendlysections")
* #InvalidateRoute("get_sections")
* #InvalidateRoute("get_section", params={"id" = {"expression"="id"}})")
*
* Route: delete_section
*/
public function deleteSectionsAction($id)
{
$em = $this->getDoctrine()->getManager();
if ($section = $this->getDoctrine()->getRepository(Section::class)->find($id)) {
$em->remove($section);
$em->flush();
}
}
}
After searching few days, I found the solution by myself.
In CacheKernel, I extend Symfony\Component\HttpKernel\HttpCache\HttpCache as described in FOSHttpCache documentation. But, the class must extend Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache instead as described in Symfony documentation. By consequences, the constructor change too.
To be honest, I don't know the difference between these two classes but you must use the second one if you want to have a built-in functional reverse proxy. It works now for me.
I put here the final code of src/CacheKernel.php:
<?php
namespace App;
use FOS\HttpCache\SymfonyCache\CacheInvalidation;
use FOS\HttpCache\SymfonyCache\CustomTtlListener;
use FOS\HttpCache\SymfonyCache\DebugListener;
use FOS\HttpCache\SymfonyCache\EventDispatchingHttpCache;
use FOS\HttpCache\SymfonyCache\PurgeListener;
use FOS\HttpCache\SymfonyCache\RefreshListener;
use FOS\HttpCache\SymfonyCache\UserContextListener;
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class CacheKernel extends HttpCache implements CacheInvalidation
{
use EventDispatchingHttpCache;
/**
* Overwrite constructor to register event listeners for FOSHttpCache.
*/
public function __construct(HttpKernelInterface $kernel)
{
parent::__construct($kernel, $kernel->getCacheDir());
$this->addSubscriber(new CustomTtlListener());
$this->addSubscriber(new PurgeListener());
$this->addSubscriber(new RefreshListener());
$this->addSubscriber(new UserContextListener());
if (isset($options['debug']) && $options['debug'])
$this->addSubscriber(new DebugListener());
}
/**
* Made public to allow event listeners to do refresh operations.
*
* {#inheritDoc}
*/
public function fetch(Request $request, $catch = false)
{
return parent::fetch($request, $catch);
}
}
The rest of the code don't change.
Hope it helps. See you.

Where to add parameter to Route: verification.notice {language}/email/verify

Missing required parameters for [Route: verification.notice] [URI: {language}/email/verify]
I added the laravel email verification to my project, after using localization.
But now I have the problem that the Route: verification.notice is missing a parameter. I know that I need to add/pass the app()->getLocale() parameter to the route but can't find where
I tried searching all the routes and the URLs in the project and also checked the VerificationController.php and the verify.blade.php. But I didn't find the route with the missing parameter. Also, I couldn't find someone else online with the same problem.
web.php
Route::group([
'prefix' => '{language}',
'where' => ['{language}' => '[a-Za-Z]{2}'],
'middleware' => 'SetLanguage',
],
function () {
Route::get('/', function () {
return view('welcome');
})->name('Welcome');
Auth::routes(['verify' => true]);
Route::get('/home', 'HomeController#index')->name('home');
Route::namespace('User')->group(function () {
Route::get('/profile', 'UserController#editProfile')->name('profile');
Route::put('profile', 'UserController#updateProfile');
});
Route::namespace('Admin')->group(function () {
Route::get('/dashboard', 'AdminController#index')->name('dashboard');
});
});
UserController
class UserController extends Controller
{
public function __construct()
{
$this->middleware('verified');
}
public function editProfile()
{
$user = User::where('id', Auth()->user()->id)->first();
return view('user.profile', compact('user'));
}
}
----edit----
SetLanguage.php
namespace App\Http\Middleware;
use App;
use Closure;
class SetLanguage
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
App::setLocale($request->language);
return $next($request);
}
}
Simply : override the middleware: EnsureEmailIsVerified
Create a new middleware with same name and insert : app()->getLocale();
public function handle($request, Closure $next, $redirectToRoute = null)
{
if (! $request->user() ||
($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::route($redirectToRoute ?: 'verification.notice', app()->getLocale());
}
return $next($request);
}
Modify App\Http\Kernel.php and replace :
\Illuminate\Auth\Middleware\EnsureEmailIsVerified
by
\App\Http\Middleware\EnsureEmailIsVerified::class
Finally, you can have also a problem with the verification.verify route
Override this route with a new notification class like this :
Note : URL::temporarySignedRoute can pass parameters like language
<?php
namespace App\Notifications;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class VerifyEmail extends Notification
{
/**
* The callback that should be used to build the mail message.
*
* #var \Closure|null
*/
public static $toMailCallback;
/**
* Get the notification's channels.
*
* #param mixed $notifiable
* #return array|string
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Activer mon compte client'))
->line(Lang::get('Veuillez cliquer sur le bouton ci-dessous pour activer votre compte client.'))
->action(Lang::get('Activer mon compte client'), $verificationUrl)
->line(Lang::get('Si vous n\'avez pas demandé la création d\'un compte client '.config('app.name').', ignorez simplement cet e-mail.'));
}
/**
* Get the verification URL for the given notifiable.
*
* #param mixed $notifiable
* #return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'language' => app()->getLocale(),
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
/**
* Set a callback that should be used when building the notification mail message.
*
* #param \Closure $callback
* #return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}
And add the declaration into user model :
// OVERRIDE
/**
* Send email verification.
* #call function
*/
public function sendEmailVerificationNotification() {
$this->notify(new VerifyEmail);
}
I faced the same problem before like yours.
As you said you didn't add {language}/ before the some path in your views
check your path in all view and alter it to be like this:
href="{{ route('somePath', app()->getLocale() ) }}"
make sure to alter all the pages to contain the correct path with language prefix in your views.
The workaround is to leave it as it is, but change only url to "after verification" page.
You can do it by storing user's locale while creating new user and then in VerificationController in verify method you do sth like:
$this->redirectTo = ($user->locale == 'pl') ? 'https://example.net/pl/dziekujemy' : 'https://example.net/en/thank-you';

Laravel 5.8 Custom Email and password Columns

I have a WordPress running application, which I would like to access using a separate interface that uses Laravel 5.8.(don't worry about the hashing)
As such, instead of cloning passwords back and forth, I would like to use the user_email and user_pass columns in the Laravel User model instead.
I have tried what the official docs say :
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* #param \Illuminate\Http\Request $request
*
* #return Response
*/
public function authenticate(Request $request)
{
$credentials = $request->only('user_email', 'user_pass');
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
I then edited the blade files, but no avail. Any pointers?
Laravel provides a way to change the default columns for auth (email, password) by overriding some functions.
In your User model add this function that overrides the default column for password:
App/User.php
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->user_pass;
}
And, in your LoginController change from email to user_email
App/Http/Controllers/Auth/LoginController.php
/**
* Get the login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'user_email';
}
Now you have overridden the default columns used by Laravel's Auth logic. But you are not finished yet.
LoginController has a function that validates the user's input and the password column is hardcoded to password so in order to change that, you also need to add these functions in LoginController:
App/Http/Controllers/Auth/LoginController.php
/**
* Validate the user login request.
*
* #param \Illuminate\Http\Request $request
* #return void
*
* #throws \Illuminate\Validation\ValidationException
*/
protected function validateLogin(Request $request)
{
$request->validate([
$this->username() => 'required|string',
'user_pass' => 'required|string',
]);
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only($this->username(), 'user_pass');
}
Next step is to create a custom Provider, let's call it CustomUserProvider that will be used instead of the default one EloquentUserProvider and where you will override the password field.
App/Providers/CustomUserProvider.php
<?php
namespace App\Providers;
class CustomUserProvider extends EloquentUserProvider
{
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('user_pass', $credentials))) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (Str::contains($key, 'user_pass')) {
continue;
}
if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
}
return $query->first();
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['user_pass'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
}
Now that you extended the default provider you need to tell Laravel to use this one instead of EloquentUserProvider. This is how you can do it.
App/Providers/AuthServiceProvider.php
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
$this->app->auth->provider('custom', function ($app, $config) {
return new CustomUserProvider($app['hash'], $config['model']);
});
}
Finally update the config information config/auth.php and change the driver from eloquent to custom (that's how I named it above; you can change it to whatever you want). So the config/auth.php file should have this bit:
'providers' => [
'users' => [
'driver' => 'custom',
'model' => App\User::class,
],
],
Hope it helps!
Regards
It would be up and working, If you can just use sessions here instead of using Auth::attempt just like working on core PHP.

I would like to ask about PHPUnit of Drupal 8

I created Block in Drupal 8 with a custom module.
Is it possible to implement this with PHPUnit?
If you can implement it please tell me how.
I want to realize the test with PHPUnit below.
I would be pleased if you could reply just whether it was possible or not.
moduleNameBlock.php
/**
* #file
* create block
*/
namespace Drupal\moduleName\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Url;
/**
*
* Provides a 'testBlock' block.
* #Block(
* id = "test_block",
* admin_label = #Translation("Test"),
* category = #Translation("Menu"),
* )
*/
class moduleNameBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build()
{
$build = [];
$url = '';
$nid = '';
$nid = $this->getCurrentUserNode();
if ( !empty($nid) ) {
$url = Url::fromRoute('entity.node.canonical', ['node' => $nid]);
}
$block = [
'#theme' => 'block_theme',
'#url' => $url,
'#nid' => $nid,
'#cache' => [
'max-age' => 0
]
];
$build['test_block'] = $block;
return $build;
}
/**
* The node associated with the user
* #return nid
*/
private function getCurrentUserNode() {
$user_id = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
$nid = $user_id->get('field_name')->getValue();
return $nid[0]['target_id'];
}
}
Yes this is possible by writing a PHPUnit Functional test.
In your module directory create the following structure /tests/src/Functional then create a file like ModuleNameBlockTest.php then you can place the block in the setUp function and create tests to test the block.
<?php
namespace Drupal\Tests\my_module_name\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Class ModuleNameBlockTest.
*
* #package Drupal\Tests\my_module_name\Functional
* #group my_group
*/
class ModuleNameBlockTest extends BrowserTestBase {
/**
* Modules to enable.
*
* #var array
*/
public static $modules = ['block', 'my_module_name'];
/**
* {#inheritdoc}
*/
protected function setUp() {
parent::setUp();
$adminUser = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($adminUser);
$this->drupalPlaceBlock('my_block_name');
$this->drupalLogout($adminUser);
}
/**
* Test the block.
*/
public function testMyAwesomeBlock() {
// Your test logic here.
}
}
You can always look into the source code of Drupal for some examples. E.g. UserBlocksTest.php of the core user module.

Inserting a form into a block in Drupal?

Is there any command or method that I can use to insert the contents of a form (e.g. the user registration form) into a block?
In Drupal 7, it looks like this:
function yourmodule_block_view($delta='')
{
switch($delta) {
case 'your_block_name':
$block['subject'] = null; // Most forms don't have a subject
$block['content'] = drupal_get_form('yourmodule_form_function');
break;
}
return $block;
}
The form array returned by drupal_get_form will be automatically rendered.
yourmodule_form_function is a function (in your module or an existing Drupal module) that returns the form array;
drupal_get_form($form_id) - put it in a module's hook_block ($op=='view') or even... shudder... inside a block with PHP filter on.
You need to find the form id first - look for a hidden input with the name form_id within the form. Its value should be the the form id.
Also, you could simply use the Form Block module.
Drupal 8+ solution
Create the form. Then, to create the block use something like this:
<?php
namespace Drupal\my_module\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\my_module\Form\MyForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the My Block block.
*
* #Block(
* id = "my_block",
* admin_label = #Translation("My Block")
* )
*/
class MyBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The form builder.
*
* #var \Drupal\Core\Form\FormBuilder
*/
protected $formBuilder;
/**
* Constructs a new MyBlock object.
*
* #param array $configuration
* A configuration array containing information about the plugin instance.
* #param string $plugin_id
* The plugin_id for the plugin instance.
* #param mixed $plugin_definition
* The plugin implementation definition.
* #param \Symfony\Component\DependencyInjection\ContainerInterface $container
* Our service container.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ContainerInterface $container) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->formBuilder = $container->get('form_builder');
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container
);
}
/**
* {#inheritdoc}
*/
public function build() {
$form = $this->formBuilder->getForm(MyForm::class);
return $form;
// // Or return a render array.
// // in mytheme.html.twig use {{ form }} and {{ data }}.
// return [
// '#theme' => 'mytheme',
// "#form" => $form,
// "#data" => $data,
// ];
}
}

Resources