Undefined function 'field_widget_instance - drupal-8

Getting error in Drupal 9 Migration custom module
function sun_link_field_process($element, $form_state, $complete_form) {
$instance = field_widget_instance($element, $form_state);
$settings = $instance['settings'];

This is Drupal 7 field API, it just needs converting to D9.x. Note too, it's very different now and needs to be in a class and use Annotations to be defined properly.
I see someone called "samtech" asked about this exact same function a couple of days ago;
function sun_link_field_process($element, $form_state, $complete_form) {
$instance = **field_widget_instance**($element, $form_state);
$settings = $instance['settings'];
$attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $settings['attributes'];
$element['attributes']['link_classes'] = array(
'#type' => 'textfield',
'#title' => t('Custom link classes'),
'#description' => t('A space delimited list of custom classes to be applied to the link.'),
'#default_value' => isset($attributes['link_classes']) ? $attributes['link_classes'] : '',
'#field_prefix' => 'class="',
'#field_suffix' => '"',
);
return $element;
}
Assuming this is the full function, just look to /core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php for how to apply a "process" on a form widget element in D9. But note, that your function exists at all suggests that none of the rest of the field is defined correctly either?
/**
* Form API callback: Processes an image_image field element.
*
* Expands the image_image type to include the alt and title fields.
*
* This method is assigned as a #process callback in formElement() method.
*/
public static function process($element, FormStateInterface $form_state, $form) {

Related

Create custom module for render custom forms through a controller in Drupal 8

I need to render a custom form which is created using Drupal\Core\Form\FormBase and Drupal\Core\Form\FormStateInterface through a controller in custom Drupal 8 module. Is there any guidence or reference to follow to do this?
Actually I tried to render form directly and through a controller. But both ways are not working. Only render the submit button. I refer the drupal 8 documentation also. But I couldn't find a solution for this. Please be kind enough to find my coding samples below. If there are anything wrong. Please correct me.
my_module.routing.yml
partner.content:
path: '/partner'
defaults:
_controller: '\Drupal\partner\Controller\PartnerController::add'
_title: 'Add Partner'
requirements:
_permission: 'access content'
partner.addform:
path: '/partner/add'
defaults:
_form: '\Drupal\partner\Form\AddForm'
_title: 'Add Partner'
requirements:
_permission: 'access content'
AddForm.php
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class AddForm extends FormBase
{
/**
* Returns form id
*
* #return string
*/
public function getFormId(): string
{
return 'my_module_add_form';
}
/**
* Build form array
*
* #param array $form
* #param FormStateInterface $formState
* #return array
*/
public function buildForm(array $form, FormStateInterface $form_state): array
{
// First name
$form['first_name'] = [
'#type' => 'textField',
'#title' => t('First Name'),
'#required' => true,
];
// Other input fields...
$form['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save Changes'),
'#button_type' => 'primary',
);
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {}
public function submitForm(array &$form, FormStateInterface $form_state) {}
}
MyModuleController.php
<?php
namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\my_module\Form\AddForm;
class MyModuleController extends ControllerBase
{
public function add()
{
$addForm = new AddForm();
$form = \Drupal::formBuilder()->getForm($addForm);
return [
'#theme' => 'form_my_module_add',
'#form' => $form,
];
}
}
Happy to find out the solution with Hemantha Dhanushka on my comment.
To make it clear this question has a correct answer, here I past the validated comment.
I would recommend you to use the first approach (using routing::_form instead
of Controller). Also, it seems you use the wrong #type for your
first_name field. Try textfield instead of textField.
Also, for people who want to go further, here are some links to implement a proper
routing::_form approach to expose a form as a page instead of using a Controller: https://www.valuebound.com/resources/blog/step-by-step-method-to-create-a-custom-form-in-drupal-8.
For people looking for more help about existing Form Element Reference (textfield, checkboxes, entity_autocomplete, ...) here is an excellent up-to-date article https://drupalize.me/tutorial/form-element-reference?p=2766
You can use buildForm() method for it. Check below code example:
public function add()
{
$form_state = new Drupal\Core\Form\FormState();
$form_state->setRebuild();
$form = \Drupal::formBuilder()->buildForm('Drupal\my_module\Form\AddForm', $form_state);
return [
'#theme' => 'form_my_module_add',
'#form' => $form,
];
}
Reference: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Form!FormBuilder.php/function/FormBuilder::getForm/8.2.x

Retrieve a taxonomy term in the buildrow function of a drupal 8 custom entity

I have built a custom entity that works well. One of my fields is a taxonomy but I can not retrieve the name of the term in the buildRow(EntityInterface $entity) function which displays my records.
For a simple string field I do: $row['foo'] = $entity->foo->value;
How to do a taxonomy term that is an entity_reference: $row['bar'] = $entity->BAR_TERM_NAME;
Thank you for your help.
To work as requested you need 3 things:
Implements an entity_reference field in your custom Entity.
Add a getter methode for you field.
Retrieve your field in your custom ListBuilder -> buildRow().
Check the Drupal 8 documentation about FieldTypes, FieldWidgets and FieldFormatters.
Implements an entity_reference field
Your field foo in your Entity should be generated using the entity_reference field type.
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
// Some code ...
$fields['foo'] = BaseFieldDefinition::create('entity_reference')
->setLabel($this->t('Foo field'))
->setDescription($this->t('The Foo field.'))
->setSetting('target_type', 'taxonomy_term')
->setSetting('handler', 'default')
->setSetting('handler_settings', ['target_bundles' => ['vocabulary_id' => 'vocabulary_id']])
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'vocabulary_id',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'options_select',
'weight' => 40,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
// Some code ...
}
You should then replace the 3 vocabulary_id by the vocabulary that you wanna links.
Add a getter
In the same Class as your baseFieldDefinitions.
// Some code ...
public function getFoo() {
return $this->get('foo')->value;
}
// Some code ...
Retrieve the field
In your ListBuilder Class.
public function buildRow(EntityInterface $entity) {
// Some code ...
$row['foo'] = $entity->getFoo();
// Some code ...
}
Hopes it will help you !

Cakephp 3 - Unit test validationDefault

I'm currently trying to write a unit test for the following model:
<?php
namespace App\Model\Table;
use App\Model\Entity\User;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
/**
* Users Model
*
* #property \Cake\ORM\Association\HasMany $Comments
* #property \Cake\ORM\Association\BelongsToMany $Albums
*/
class UsersTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('users');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasMany('Comments', [
'foreignKey' => 'user_id'
]);
$this->belongsToMany('Albums', [
'foreignKey' => 'user_id',
'targetForeignKey' => 'album_id',
'joinTable' => 'users_albums'
]);
}
/**
* #Author: Mark van der Laan
* #Date: 23-02-2016
* #Description: Validating rules for the user model. Some additional, more complex validation rules are added.
* #param \Cake\Validation\Validator $validator Validator instance.
* #return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator)
{
// id
$validator
->integer('id')
->allowEmpty('id', 'create');
// username
$validator
->requirePresence('username', 'create')
->notEmpty('username')
// Enabled, just in case that the username will be an email address
->email('username')
->add('username', [
'length' => [
'rule' => ['minLength', 7],
'message' => 'Username needs to be at least 7 characters long!',
]
]);
// password
$validator
->requirePresence('password', 'create')
->notEmpty('password')
->add('password', [
'length' => [
'rule' => ['minLength', 7],
'message' => 'Password needs to be at least 7 characters long!',
]
]);
// sign_in_count
$validator
->integer('sign_in_count')
->requirePresence('sign_in_count', 'create')
->notEmpty('sign_in_count');
// ip address
$validator
->allowEmpty('current_sign_in_ip')
->requirePresence('current_sign_in_ip', 'create')
// Currently checking for both IPv4 and IPv6 addresses
->ip('current_sign_in_ip', 'both');
// active
$validator
->boolean('active')
->requirePresence('active', 'create')
->allowEmpty('active');
return $validator;
}
/**
* Returns a rules checker object that will be used for validating
* application integrity.
*
* #param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* #return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique(['username']));
return $rules;
}
}
It is important for me to test the validationDefault method which I try to do with the following code snippet:
public function testValidationDefault()
{
$data = ['username' => 'adminadmin#mixtureweb.nl',
'password' => 'testtest123',
'sign_in_count' => 0,
'current_sign_in_ip' => '127.0.0.1',
'active' => 'true'
];
$this->assertTrue($this->Users->save($data));
// $this->assertTrue($data);
}
As I try to do this, this will throw an error saying that I shouldn't pass an array to assertTrue method. Therefore, I'm trying to find examples but I couldn't find anything. Has anyone some references where I can find how to unit test validation rules? (so far I couldn't find anything in the documentation)
Update
public function testValidationDefault()
{
$data = ['username' => 'adminadmin#mixtureweb.nl',
'password' => 'testtest123',
'sign_in_count' => 0,
'current_sign_in_ip' => '127.0.0.1',
'active' => true
];
$user = $this->Users->newEntity($data);
$saved = $this->Users->save($user);
$this->assertTrue($saved);
// $this->assertTrue($data);
}
This will give 'Failed asserting that App\Model\Entity\User Object &0000000011b3c53b0000000040aca14b is true'. Does anyone know what I'm doing wrong?
Take a look at what Table::save() returns, it's \Cake\Datasource\EntityInterface|bool. On success it returns the persisted entity, on failure it returns boolean false. So your save operation succeeds and it will return an entity, hence the error.
If you want to test validation, you should either use the validator object that your table class offers (Table::validationDefault() via Table::validator()), or use Table::patchEntity() or Table::newEntity() and test the value of Entity:errors().
Patching/creating entities is where validation in the model layer happens, the saving process will only apply application rules.
public function testValidationDefault()
{
$data = [
'username' => 'adminadmin#mixtureweb.nl',
'password' => 'testtest123',
'sign_in_count' => 0,
'current_sign_in_ip' => '127.0.0.1',
'active' => true
];
$user = $this->Users->newEntity($data);
$this->assertEmpty($user->errors()); // empty = no validation errors
}
See also
Cookbook > Validation > Validating Data
Cookbook > Validation > Validating Entities
Cookbook > Database Access & ORM > Validating Data > Validation vs. Application Rules

Hook a twig template to a block in Drupal 8

I created a module which creates a custom block :
<?php
/**
* Provides a 'SLS Block' Block
*
* #Block(
* id = "SLS-Subheader",
* admin_label = #Translation("SLS Subheader"),
* )
*/
namespace Drupal\subheader\Plugin\Block;
use Drupal\Core\Block\BlockBase;
class SubheaderBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build() {
return array(
'#title' => "test",
);
}
}
?>
The module name is "subheader"
In my subheader.module i want to hook a specific template:
<?php
/**
* Implements hook_theme().
*/
function subheader_theme() {
return array(
'slssubheader' => array(
'variables' => array('pierre' => NULL),
'template' => 'specifictemplate',
),
);
}
I tried all kind of naming convention for the function name and the array key, but always unsuccesful. It never hook the template to specifictemplate.html.twig
Anyone has an idea??
Thanks a LOOOOTTT
Pierre
I had the same problem, though probably a different cause. Google lead me to your question though. The issue with your code is the missing #theme key in your build method I believe:
public function build() {
return array(
'#title' => "test",
'#theme' => 'slssubheader' // this one
);
}
In my case I had to search for a couple of hours before I found out I accidentally added a custom namespace to my .module file. Drupal doesn't like that and didn't recognize any of my hooks.

Accessing Model in CakePHP Controller Test

I'm new to CakePHP, and I just started writing my first tests. Usually doing Ruby on Rails, my approach to testing a Controller::create action would be to call the create action, and then comparing the number of models before and after that call, making sure it increased by one.
Would anyone test this any other way?
Is there an easy (builtin) way to access models from a ControllerTest in CakePHP? I couldn't find anything in the source, and accessing it through the Controller seems wrong.
I ended up doing something like this:
class AbstractControllerTestCase extends ControllerTestCase {
/**
* Load models, to be used like $this->DummyModel->[...]
* #param array
*/
public function loadModels() {
$models = func_get_args();
foreach ($models as $modelClass) {
$name = $modelClass . 'Model';
if(!isset($this->{$name})) {
$this->{$name} = ClassRegistry::init(array(
'class' => $modelClass, 'alias' => $modelClass
));
}
}
}
}
Then my tests inherit from AbstractControllerTestCase, call $this->loadModels('User'); in setUp and can do something like this in the test:
$countBefore = $this->UserModel->find('count');
// call the action with POST params
$countAfter = $this->UserModel->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
Note that I'm new to CakePHP but came here with this question. Here's what I ended up doing.
I got my idea from #amiuhle, but I just do it manually in setUp, like how they mention in the model tests at http://book.cakephp.org/2.0/en/development/testing.html.
public function setUp() {
$this->Signup = ClassRegistry::init('Signup');
}
public function testMyTestXYZ() {
$data = array('first_name' => 'name');
$countBefore = $this->Signup->find('count');
$result = $this->testAction('/signups/add',
array(
'data' => array(
'Signup' => $data)
)
);
$countAfter = $this->Signup->find('count');
$this->assertEquals($countAfter, $countBefore + 1);
}
I am not sure why it is necessary to test how many times a model is called or instantiated from the controller action.
So, if I was testing Controller::create... my ControllerTest would contain something like:
testCreate(){
$result = $this->testAction('/controller/create');
if(!strpos($result,'form')){
$this->assertFalse(true);
}
$data = array(
'Article' => array(
'user_id' => 1,
'published' => 1,
'slug' => 'new-article',
'title' => 'New Article',
'body' => 'New Body'
)
);
$result = $this->testAction(
'/controller/create',
array('data' => $data, 'method' => 'post')
);
if(!strpos($result,'Record has been successfully created')){
$this->assertFalse(true);
}
}
The main things you want to test for is whether you are getting the right output for the input. And you can use xDebug profiler to easily find out what classes get instnantiated in a particular action and even how many times. There is no need to test for that manually!