In this article, we'll create custom table in database and create custom form with CRUD Operations. this just is an example how to use database api and form api for create tips programmatically without Views module.
Create a module
In Drupal 8, it is necessary to create an info.yml
file that contains the metadata for every custom module. you will need to create the mymodule.info.yml
file under the modules/custom/mymodule
folder. Inside this file enter following:
name: 'My Module'
description: 'Custom Module'
package: Custom
type: module
core: 8.x
Once the folder and file has been created, you can go to your Drupal dashboard and enable the custom module we have just created.
Create Custom Table in Database
To create a table in the database. I have created mymodule.install file, in this file I added a table called mytable.
hook_schema()
helps to create the table and their fields into the database. In our custom form the crud database will be stored in mytable as the table name. If the value is stored in the database, it helps in performing CRUD operations,like edit or delete.
<?php
/**
* create table mytable
*/
function mymodule_schema() {
$schema['mytable'] = array(
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'first_name' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'last_name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'email' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'phone' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'fid' => array(
'type' => 'int',
'length' => 20,
'not null' => FALSE,
),
'select' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'message' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'primary key' => array(
'id',
),
);
return $schema;
}
The result in the database is like this:
Create the routes
To create routes I have created mymodule.routing.yml file, in this file I added all the routes for add/edit data, delete data, show data and get all data.
mymodule.display_data:
path: '/admin/mymodule/index'
defaults:
_controller: '\Drupal\mymodule\Controller\DisplayTableController::index'
_title: 'All Data'
requirements:
_permission: 'access content'
mymodule.show_data:
path: '/admin/mymodule/{id}/show'
defaults:
_controller: '\Drupal\mymodule\Controller\MydataController::show'
_title: 'Show Data'
requirements:
_permission: 'access content'
mymodule.delete_form:
path: '/admin/mymodule/{id}/delete'
defaults:
_form: '\Drupal\mymodule\Form\DeleteForm'
_title: 'DeleteForm'
requirements:
_access: 'TRUE'
mymodule.add_form:
path: '/admin/mymodule/add'
defaults:
_title: 'Add/Edit Data'
_form: '\Drupal\mymodule\Form\MyModuleForm'
requirements:
_permission: 'access content'
Save data in database
The complete code for create our form and insert data is shown below. so let's create src/Form/MyModuleForm.php class.
this class contains :
getFormId(): This needs to return a string that is the unique ID of your form. Namespace the form ID based on your module's name.
buildForm(): This returns a Form API array that defines each of the elements of your form.
submitForm(): collect data and save the data to the database.
validateForm(): used to validate the data that's being collected.
<?php
namespace Drupal\mymodule\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Symfony\Component\HttpFoundation\RedirectResponse;
class MyModuleForm extends FormBase
{
/**
* {@inheritdoc}
*/
public function getFormId()
{
return 'mymodule_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state)
{
$form['first_name'] = [
'#type' => 'textfield',
'#title' => $this->t('first name'),
'#required' => true,
'#size' => 60,
'#default_value' => ' ',
'#maxlength' => 128,
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['last_name'] = [
'#type' => 'textfield',
'#title' => $this->t('last name'),
'#required' => true,
'#size' => 60,
'#default_value' => ' ',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('email'),
'#required' => true,
'#default_value' => ' ',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['picture'] = array(
'#title' => t('picture'),
'#description' => $this->t('Chossir Image gif png jpg jpeg'),
'#type' => 'managed_file',
'#required' => true,
'#upload_location' => 'public://images/',
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg')),
);
$form['phone'] = [
'#type' => 'tel',
'#title' => $this->t('phone'),
'#required' => true,
'#default_value' => ' ',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['select'] = [
'#type' => 'select',
'#title' => $this
->t('Select element'),
'#options' => [
'1' => $this
->t('One'),
'2' => [
'2.1' => $this
->t('Two point one'),
'2.2' => $this
->t('Two point two'),
],
'3' => $this
->t('Three'),
],
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('message'),
'#required' => true,
'#default_value' => ' ',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('save'),
'#buttom_type' => 'primary'
];
return $form;
}
/**
* @param array $form
* @param FormStateInterface $form_state
*/
public function validateForm(array &$form, FormStateInterface $form_state)
{
if (is_numeric($form_state->getValue('first_name'))) {
$form_state->setErrorByName('first_name', $this->t('Error, The First Name Must Be A String'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state)
{
$picture = $form_state->getValue('picture');
$data = array(
'first_name' => $form_state->getValue('first_name'),
'last_name' => $form_state->getValue('last_name'),
'email' => $form_state->getValue('email'),
'phone' => $form_state->getValue('phone'),
'select' => $form_state->getValue('select'),
'message' => $form_state->getValue('message'),
'fid' => $picture[0],
);
// save file as Permanent
$file = File::load($picture[0]);
$file->setPermanent();
$file->save();
// insert data to database
\Drupal::database()->insert('mytable')->fields($data)->execute();
// show message and redirect to list page
\Drupal::messenger()->addStatus('Succesfully saved');
$url = new Url('mymodule.display_data');
$response = new RedirectResponse($url->toString());
$response->send();
}
}
our custom form is like this:
display all data from database in a custom table
The complete code for display data is shown below. so let's create src/Controller/DisplayTableController.php class.
this class contains :
index() method: In this method, we will display the output in the table format. We have used the standard way of displaying the values of the row in table format.
<?php
namespace Drupal\mymodule\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Class DisplayTableController
* @package Drupal\mymodule\Controller
*/
class DisplayTableController extends ControllerBase
{
public function index()
{
//create table header
$header_table = array(
'id' => t('ID'),
'first_name' => t('first name'),
'last_name' => t('last name'),
'email' => t('Email'),
'phone' => t('phone'),
'view' => t('View'),
'delete' => t('Delete'),
'edit' => t('Edit'),
);
// get data from database
$query = \Drupal::database()->select('mytable', 'm');
$query->fields('m', ['id', 'first_name', 'last_name', 'email', 'phone']);
$results = $query->execute()->fetchAll();
$rows = array();
foreach ($results as $data) {
$url_delete = Url::fromRoute('mymodule.delete_form', ['id' => $data->id], []);
$url_edit = Url::fromRoute('mymodule.add_form', ['id' => $data->id], []);
$url_view = Url::fromRoute('mymodule.show_data', ['id' => $data->id], []);
$linkDelete = Link::fromTextAndUrl('Delete', $url_delete);
$linkEdit = Link::fromTextAndUrl('Edit', $url_edit);
$linkView = Link::fromTextAndUrl('View', $url_view);
//get data
$rows[] = array(
'id' => $data->id,
'first_name' => $data->first_name,
'last_name' => $data->last_name,
'email' => $data->email,
'phone' => $data->phone,
'view' => $linkView,
'delete' => $linkDelete,
'edit' => $linkEdit,
);
}
// render table
$form['table'] = [
'#type' => 'table',
'#header' => $header_table,
'#rows' => $rows,
'#empty' => t('No data found'),
];
return $form;
}
}
our table result is like this:
Delete data from database
In this file we will perform delete operation. I have followed certain standards to delete operation, we can also user db_delete operation. The complete code for the delete operation is shown below:
<?php
namespace Drupal\mymodule\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Url;
/**
* Class DeleteForm
* @package Drupal\mymodule\Form
*/
class DeleteForm extends ConfirmFormBase
{
public $id;
/**
* {@inheritdoc}
*/
public function getFormId()
{
return 'delete_form';
}
public function getQuestion()
{
return t('Delete data');
}
public function getCancelUrl()
{
return new Url('mymodule.display_data');
}
public function getDescription()
{
return t('Do you want to delete data number %id ?', array('%id' => $this->id));
}
/**
* {@inheritdoc}
*/
public function getConfirmText()
{
return t('Delete it!');
}
/**
* {@inheritdoc}
*/
public function getCancelText()
{
return t('Cancel');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $id = NULL)
{
$this->id = $id;
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state)
{
parent::validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state)
{
$query = \Drupal::database();
$query->delete('mytable')
->condition('id', $this->id)
->execute();
\Drupal::messenger()->addStatus('Succesfully deleted.');
$form_state->setRedirect('mymodule.display_data');
}
}
When you click delete data, an delete confirmation form will be display like this to confirm if you are sure you want data or not.
Update data in database
The complete code for update data is shown below. so let's change our src/Form/MyModuleForm.php class.
I override these methods :
buildForm(): I changed default values if I have update operation.
submitForm(): I added condition to check if I have add or update operation by passing ?id=VALUE in update operations.
<?php
namespace Drupal\mymodule\Form;
use Drupal\Core\Database\Database;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Symfony\Component\HttpFoundation\RedirectResponse;
class MyModuleForm extends FormBase
{
/**
* {@inheritdoc}
*/
public function getFormId()
{
return 'mymodule_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state)
{
$conn = Database::getConnection();
$data = array();
if (isset($_GET['id'])) {
$query = $conn->select('mytable', 'm')
->condition('id', $_GET['id'])
->fields('m');
$data = $query->execute()->fetchAssoc();
}
$form['first_name'] = [
'#type' => 'textfield',
'#title' => $this->t('first name'),
'#required' => true,
'#size' => 60,
'#default_value' => (isset($data['first_name'])) ? $data['first_name'] : '',
'#maxlength' => 128,
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['last_name'] = [
'#type' => 'textfield',
'#title' => $this->t('last name'),
'#required' => true,
'#size' => 60,
'#default_value' => (isset($data['last_name'])) ? $data['last_name'] : '',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['email'] = [
'#type' => 'email',
'#title' => $this->t('email'),
'#required' => true,
'#default_value' => (isset($data['email'])) ? $data['email'] : '',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['picture'] = array(
'#title' => t('picture'),
'#description' => $this->t('Chossir Image gif png jpg jpeg'),
'#type' => 'managed_file',
'#required' => true,
'#default_value' => (isset($data['fid'])) ? [$data['fid']] : [],
'#upload_location' => 'public://images/',
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg')),
);
$form['phone'] = [
'#type' => 'tel',
'#title' => $this->t('phone'),
'#required' => true,
'#default_value' => (isset($data['phone'])) ? $data['phone'] : '',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['select'] = [
'#type' => 'select',
'#title' => $this
->t('Select element'),
'#options' => [
'1' => $this
->t('One'),
'2' => [
'2.1' => $this
->t('Two point one'),
'2.2' => $this
->t('Two point two'),
],
'3' => $this
->t('Three'),
],
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12'],
'#default_value' => (isset($data['select'])) ? $data['select'] : '',
];
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('message'),
'#required' => true,
'#default_value' => (isset($data['message'])) ? $data['message'] : '',
'#wrapper_attributes' => ['class' => 'col-md-6 col-xs-12']
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('save'),
'#buttom_type' => 'primary'
];
return $form;
}
/**
* @param array $form
* @param FormStateInterface $form_state
*/
public function validateForm(array &$form, FormStateInterface $form_state)
{
if (is_numeric($form_state->getValue('first_name'))) {
$form_state->setErrorByName('first_name', $this->t('Error, The First Name Must Be A String'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state)
{
$picture = $form_state->getValue('picture');
$data = array(
'first_name' => $form_state->getValue('first_name'),
'last_name' => $form_state->getValue('last_name'),
'email' => $form_state->getValue('email'),
'phone' => $form_state->getValue('phone'),
'select' => $form_state->getValue('select'),
'message' => $form_state->getValue('message'),
'fid' => $picture[0],
);
// save file as Permanent
$file = File::load($picture[0]);
$file->setPermanent();
$file->save();
if (isset($_GET['id'])) {
// update data in database
\Drupal::database()->update('mytable')->fields($data)->condition('id', $_GET['id'])->execute();
} else {
// insert data to database
\Drupal::database()->insert('mytable')->fields($data)->execute();
}
// show message and redirect to list page
\Drupal::messenger()->addStatus('Succesfully saved');
$url = new Url('mymodule.display_data');
$response = new RedirectResponse($url->toString());
$response->send();
}
}
The result of updating data is like this:
Show data by ID
The complete code for display data by id is shown below. so let's create src/Controller/MydataController .php class.
this class contains :
show() method: In this method, we will display the output in the markup format.
<?php
namespace Drupal\mymodule\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Database;
use Drupal\file\Entity\File;
/**
* Class MydataController
* @package Drupal\mymodule\Controller
*/
class MydataController extends ControllerBase
{
/**
* @return array
*/
public function show($id)
{
$conn = Database::getConnection();
$query = $conn->select('mytable', 'm')
->condition('id', $id)
->fields('m');
$data = $query->execute()->fetchAssoc();
$full_name = $data['first_name'] . ' ' . $data['last_name'];
$email = $data['email'];
$phone = $data['phone'];
$message = $data['message'];
$file =File::load($data['fid']);
$picture = $file->url();
return [
'#type' => 'markup',
'#markup' => "<h1>$full_name</h1><br>
<img src='$picture' width='100' height='100' /> <br>
<p>$email</p>
<p>$phone</p>
<p>$message</p>"
];
}
}
The result of single data is like this:
Next steps
- Clear your Drupal 8 caches. To do this I use this Drush command:
drush cr
if you don’t currently use Drush, I highly recommend using it, or the Drupal Console. - I hope you found this article useful. let me know if you have any questions and I’ll be happy to answer them.