- Drupal 8 States Not Empty
- Drupal 8 Statistics
- Drupal 8 States Map
- Drupal 8 States Api
- Drupal 8 States Required
- Drupal 8 States Form Api
Drupal 8 was recently released after years of development and testing. A majority of the Drupal core was rewritten from the ground up in this latest version - a radical approach to modernizing the platform on various fronts and easing both development and content management.
Previous versions of Drupal were largely revised and improved upon releases of the initial build. Drupal 8, however, has cast most of this initial core aside in favor of leveraging Symfony components as a foundation.
While there have been numerous changes and your favorite functions or tools might have been updated or removed, this guide series should provide a breakdown of popular functions within the new release. Absorbing all of these new concepts at once might be intimidating to those who are used to years of the same development patterns. So we're going to ease into it with a simple example: a custom form.
Forms in Drupal - 7 vs 8
Lets assume you have a module (called testmodule) in Drupal 7 that creates a specific URL with a form that asks the user to pick their favorite fruit from a list of options. Doing this in Drupal 7 might have looked something like this:
Nothing to it, right? Many modules will either use the above or similar patterns when needing to display a form at a specific URL.
However, in Drupal 8, hook_menu and many other hooks have been removed. Additionally, we can't directly access values in the $form_state array and drupal_map_assoc has been removed. We also can't overwrite and assign a value to $form_state['redirect'] anymore.
This doesn't mean that you can't implement custom forms or related functionality with the new release. It just means the approach for doing so has changed. For long-time Drupalers the changes may feel alien but with some practice and understanding you'll see why the paradigm shift in Drupal 8 is an important one for moving forward.
First, before Drupal 8 will even recognize your module, you will need to rename your testmodule.info file to testmodule.info.yml, which should look like:
Secondly - and perhaps one of the biggest changes to get accustomed to - you do not need a module file (at least not for our purposes). You would still need one if you plan to use something like hook_form_alter but the main point to keep in mind is a lot of code you will write in Drupal 8 does not live in this file anymore.
Object-Oriented Programming, Autoloading, & Namespaces
If you are unfamiliar with object-oriented programming (OOP), you will need to get acquainted with it if you'd like to write or contribute code to the Drupal platform. However the rewritten Drupal core consisting of Symfony components provides easy-to-use interfaces, plugins, services, classes, and objects for extending Drupal. As a result the code is more structured, predictable, and testable. And trust me, once you start using this new framework, you will find it hard to return to Drupal 7.
So let's start with defining the path to our form. Since we can't use hook_menu, we need to create a routing file:
Drupal 8 States Not Empty
testmodule.fruit_form defines a router item and can be used internally with a direct reference. We set the page title and controller for this route to DrupaltestmoduleFormFruitForm. By using the _form indicator, we are informing Drupal that this is a form based controller and the route will expect a controller that implements DrupalCoreFormFormInterface.
Now we need to identify a new location for our code as we can no longer use the .module file. Every module in Drupal 8 is consistent with how it declares/implements interfaces, plugins, classes, entities, and more. If you are not familiar with the PSR standards, you can learn more about them here.
First, we'll start by defining our form controller. You will need to create a src directory in your module and a Form directory within that. Create your FruitForm.php file here. Since Drupal 8 implements the PSR-4 autoloading standard, it will understand this directory structure.
The next thing you will need to understand is the concept of namespaces and the use keyword.
Namespaces and the USE Keyword
In a nutshell, namespaces help define paths to your code and the autoloader, and the use keyword lets you alias and/or import namespaces for reference in your code.
If you did not alias DrupalCoreFormFormStateInterface, for example, your code may resemble something like:
It is highly advised that you employ the use keyword instead:
use in this context tells PHP to import those classes and you can refer to them by class name instead of their full namespace path.
While this may be a basic example it provides you with an idea of what is possible with Drupal 8. Leveraging third party libraries is a lot easier now than in Drupal 7 thanks to advances in PHP 5.5+, the PSR-4 autoloading standard, and the great work being done by the Framework InterOp Group.
Bringing it All Together
Alright, let's define that form! A form should extend the FormBase class, which implements the FormInterface interface. We need to implement 4 methods to meet the requirement of FormInterface:
As you may have guessed, construction, validation, and submission are now encapsulated within your class instead of in loosely defined procedural functions. Much of the FormAPI code is the same so we're able to port it from Drupal 7 with a few changes.
The buildForm() method is similar to the previously used drupal_get_form callback with some differences.
First, the second argument is passed to the function and must be an instance of FormStateInterface. $form_state in Drupal 8 is no longer just an array, it is an object with methods and properties. Gone are the days of accessing and modifying $form_state keys. We also cannot read submitted values directly but instead, must use getValue (or getValues). Finally, we have to use the setRedirect method for the same reasons. This enforcement ensures that the code adheres to a consistent standard when dealing with any class that extends FormBase.
You'll notice that the drupal_map_assoc function is no longer present in Drupal 8. However, array_combine can be used as a substitute as it serves the same purpose of creating an associative array. Putting it all together, we see our form when we visit the testmodule/ask-user URL in Drupal 8:
Sweet! As a bonus, let's assume we wanted to redirect the user, following their submission, to some place other than the homepage (maybe 'create an account'?) We can do this using the router item instead of a raw URL. In fact, it is the enforced behavior in Drupal 8:
While this looks like a subtle change on the surface, think of the implications of NOT using a raw URL value here. By referring to the router item we are letting the system resolve the path. This prevents our redirect from breaking if the URL was to change.
Drupal 8 Statistics
If you do want to redirect to a path, this option is still available. Create a node and set its path as '/testmodule/thanks'. The FormState object has a setRedirectUrl method but we must pass our argument as an instance of DrupalCoreUrl - and you guessed it, we can import it using use and pass our node URL to it. In return, we get an instance of URL, which we can pass to setRedirectUrl.
Drupal 8 States Map
We'll be covering other Drupal 8 concepts (like testing with SimpleTest, Behat, and PHPUnit, creating custom form elements, fields and field formatters, the DrupalConsole, etc.) in future posts in this series. Join the conversation by commenting below with any questions or feedback on using these Drupal 8 constructs. Until then...
Drupal 8 States Api
Get the Example Code
Drupal 8 States Required
Feel free to download this example and give it a whirl on your machine.