Drupal 8 form element with #autocreate taxonomy term - php

I made module with form, that use autocomplete field like that:
$form['field_taxonomy_tags'] = [
'#type' => 'entity_autocomplete',
'#target_type' => 'taxonomy_term',
'#selection_settings' => [
'target_bundles' => array('tags'),
],
'#autocreate' => array(
'target_bundles' => array('tags'),
'bundle' => ('tags'),
),
'#title' => ('tags'),
'#tags' => TRUE,
];
Autocomplete works fine, and i can add taxonomy terms from tags vocabulary easily. But there is some problem with #autocreate option i think. Have searched all documentation, and code inside drupal core. Entity is never created ;/
When i try to get value from this field, my browser is dead... there is some entity type variable, but huge.
After some debugging i found way to get it work, but im not happy about it :) Very strange, maybe some of you guys can help me to find better way?
public function submitForm(array &$form, FormStateInterface $form_state) {
$tags = $form_state->getValue('field_taxonomy_tags');
foreach ($tags as $tag)
{
if(is_object($tag['entity']))
{
$tag['entity']->save();
}
}
}
As you can see, I need to save those tags manually, dont know why ;/ Without it, there is no term created.

It is better way. I dont need to save every tag, its enough if we attach them to a node. Its entity object, that can be passed as node value, and after that, all tags will be created:
$node = Node::create(array(
'type' => 'YOUR_content_type',
'title' => $form_state->getValue('title')
));
$fieldNames = array_keys($node->getFieldDefinitions());
$values = $form_state->getValues();
// be aware with that, i use this loop for testing because i have same names
// you can use $node->set('content type field name', $value); directly without any field definitions
foreach ($values as $key=>$value)
{
if(in_array($key, $fieldNames))
{
$node->set($key, $value);
}
}
// here we save all data, taxonomy entities too
$node->save();

Related

Firebase PHP: Updating also deletes the other children

I am using the Firebase PHP Admin SDK: https://firebase-php.readthedocs.io/en/stable/realtime-database.html#update-specific-fields
Here is the example it gives to update specific fields:
$uid = 'some-user-id';
$postData = [
'title' => 'My awesome post title',
'body' => 'This text should be longer',
];
// Create a key for a new post
$newPostKey = $db->getReference('posts')->push()->getKey();
$updates = [
'posts/'.$newPostKey => $postData,
'user-posts/'.$uid.'/'.$newPostKey => $postData,
];
$db->getReference() // this is the root reference
->update($updates);
From that, I created a users class and in that I have an update function. Like so:
public function update() {
$data = array('users' => array('1' => 'David'));
$this->database->getReference()->update($data);
return true;
}
In my database I have this structure:
Now if I run that function $users->update();
It removes the other child and only leaves David. Like so:
How can I update only a specific value of a specified key without it overriding the other data?
There's nothing specific to PHP here. That's the way Realtime Database update operations work. If you want a minimal update, you have to target the deepest key that you want to update. In your case, since you're storing an array type object, the keys are the number indexes of the array items you've written. If you want to modify one of them, you need to build a reference that includes the child number you want update. In that case, none of the sibling values will be touched.
Try this instead:
$this->database->getReference('users')->update(array('1' => 'David'));
Notice here that the update is rooted at "users", and you're updating just the immediate child "1" of that.
The example on docs is a little bit hard to grasp as a beginner. I have made it simpler for you to understand.
Once you get the newPostKey, prepare the url for child and run the code. It will only change the specific fields.
$ref = 'users/' . $newPostKey;
$updates = [
'title' => 'Updated title',
'body' => 'Updated body text',
];
$set = $db->getReference($ref)->update($updates);

How to return a variable operator like $option[];

Obviously, I can't do this, but is there some way to achieve what I am trying to? I only found can not do's online, but no potential workarounds.
Here is what I am trying to do.
Currently I get the following error... "Cannot use [] for reading"
For my theme, I have a framework and the fields from that framework are built using an array that I create.
It looks something like this (minus the 300+ lines of code that I actually use)...
$options[] =
array(
'title' => 'This Field Tab Title',
'name' => 'this-field-tab-slug',
'fields' =>
array(
// ----------------------------------------------------------------------
// This Field Option Name
// ----------------------------------------------------------------------
array(
'type' => 'this_field_type',
'id' => 'this_field_types_id',
),
// ----------------------------------------------------------------------
// This Field Option Name
// ----------------------------------------------------------------------
array(
'type' => 'this_field_type',
'id' => 'this_field_types_id',
),
// ----------------------------------------------------------------------
// This Field Option Name
// ----------------------------------------------------------------------
array(
'type' => 'this_field_type',
'id' => 'this_field_types_id',
),
),
);
I am running a grouped field type, so my output has many options/fields within this grouped field/area which can then be added again and again as many times as the user needs. Then I am repeating that whole process/code again but for other taxonomies of the user's site.
So for example, the whole process above applies to post types, categories, tags, archived, etc. etc. So instead of having thousands of lines of repetitive codes, I'm trying to create my own function and pass the variables to that function.
But for the function, I find I can't return $options[];
Here is a screenshot of what I mean by the grouped field that can be added as many times as the user needs.
And here's an example of the function I am trying to create!
public static function layout_settings_config($title_name = '', $title_slug = '', $title_id = '', $query = '') {
$title_name = 'Post Type';
$title_slug = 'post-type';
$title_id = 'post_type';
$query = 'post_types';
$options[] =
array(
// all the config array codes in here...
);
return $options ??? $options[]; doesn't work.
}
Is this possible to achieve what I am trying to a different way? I'm still a little new to creating my own functions and OOP, but nothing I find online for this specific issue with a workaround.
Thanks!
$options[] is not object but it is an operation like function.
You should return $options instead.
and, by the way, when you say $options[] = something. it actually insert something inside an array called $option. so effectively you have to access your options like this.
$option[0]->title.
So I suggest Instead of making it complex like this. simply say
$option = something.

Placing proper filed name error print out for validation

I am working on my first form validation. The issue that I am having is error reporting to the use.
I used an array to set up my criteria/rules for the fields:
$validate = new Validation;
$validation = $validate->check($_POST, array(
'FirstName' => array(
'name' => 'First Name',
'required' => 'TRUE'),
'LastName' => array(
'name' => 'Last Name',
'required' => TRUE));
I would like to have an error show that tells the user he/she is missing a required field without show the field name, for example: FirstName is required. I would like to see: First Name is required.
I looped through each array:
public function check($source, $items = array())
foreach($items as $item => $rules){
foreach($rules as $rule => $rule_value){
echo "{$item} {$rule} must be {$rule_value}<br>";
}
}
}
When I echo out the loops I my criteria, however, When I would try to echo out $rule_value [0], I would only get the first letter of that array.
Any suggestions?
I think that you may get rid of the second foreach loop and do something like this:
public function check($source, $items = array())
{
// In your example you have two arrays in $items so the following
// foreach will iterate two times
foreach($items as $item => $values) {
// $item is going to be 'FirstName' on the first iteration
// and 'LastName' on the second iteration
//
// $values are going to be the array that's associated
// with 'FirstName' => array(...) and 'LastName' => array(...)
// i.e. $values = array('name' => '...', 'required' = TRUE/FALSE)
// Therefore, you can easily check if a given $item is required
// and if the $source contains that $item or not:
if ($values['required'] && empty($source[$item])) {
echo "{$values['name']} is required!";
}
// EDIT for your comment - validating the string length
if ( $values['minLength']
&& strlen($source[$item]) < $values['minLength'])
{
echo "{$values['name']} must be at least {$values['minLength']} characters.";
}
}
}
What the code above does is that it goes through all elements of $items and if it finds a required field, it then checks whether the $source has the appropriate value or not (or if it's empty) and echoes an error (you may want to do different validations, this is just to illustrate an example).
Footnote: By the way, I'm not sure how you separate the view and the model but I find it useful not to echo right from the logic functions. So in your check function I'd build up an array/object with all that validation errors and I'd return it to the view - which would take care of displaying the errors. This way, you could control where the errors appear visually as I'm thinking it is perhaps useful to highlight each form field with its error.

best way to change output of a field?

Im using drupal 7
I have a date field.
Which displays the date selected as it is supposed to.
However I would to change the display to say 'this date is almost over' under certain php validation or 'this date is now closed' under different validation.
I cant use string over reides as it doesnt accept php. Would I have to use a custom module and use hook form alter or node api?
thanks for any help
Yes, you would have to use a module to adjust how the image field is displayed. You can either create a new display format using hook_field_formatter_info() along with hook_field_formatter_view(), or you can hook an existing display using hook_field_formatter_view() by checking $display['type']. I would recommend the former.
Here is an example of how the two hooks could work together:
function modulename_field_formatter_info()
{
return array(
'modulename_formatter' => array(
'label' => t('Custom Date Format'),
'field types' => array('date'),
),
);
}
function modulename_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display)
{
if ($display['type'] != 'modulename_formatter')
return;
foreach ($items as $delta => item)
{
if ($item['date'] < time()) //Or 'datetime' or 'datestamp'
$element[$delta]['#markup'] = 'this date is now closed.';
}
return $element;
}
I might have some of the indices wrong, but they should be pretty close.
http://api.drupal.org/api/drupal/modules!field!field.api.php/function/hook_field_formatter_info/7
http://api.drupal.org/api/drupal/modules!field!field.api.php/function/hook_field_formatter_view/7

Drupal: use view output as node content

I'm writing a Drupal 7 module to display child nodes of a module in the content.
Nodes have a field parent_nodes (node reference) where one ore more nodes are selected as parents.
First, I've created a view projects with a block view display subprojects, displaying nodes of type project with a field_parent_project contextual filter.
This is my module:
<?php
function projects_preprocess_node(&$variables) {
if ($variables['type'] == 'project') {
if (isset($variables['view_mode']) && $variables['view_mode'] == 'full') {
_projects_add_subprojects($variables);
}
}
}
function _projects_add_subprojects(&$variables) {
$nid = $variables['nid'];
$view = views_get_view('projects');
$preview = $view->preview('subprojects', array($nid));
$subprojects = array(
'#title' => t('Subprojects'),
'#label_display' => 'above',
'#weight' => 10,
//'#theme' => 'field',
'#markup' => $preview,
);
if (!isset($variables['content']['subprojects'])) {
$variables['content']['subprojects'] = array();
}
$variables['content']['subprojects'][] = $subprojects;
dpm($variables['content']);
}
This is working, adding the view display output to the node's content.
Only some things aren't working:
title (label)
weight do not change display position when rendered with other contents (it's always the first, above body).
If I uncomment the '#theme' => 'field' line, title is shown as a label, but nothing is rendered. This is because the field theme is used and I guess it needs #items and does not use the #markup element.
I cannot use a children nodes as reference, but only parent nodes.
The solution must be independent to theme, so no not answer "change your theme template" or similar
How can I show children nodes in node? I'm looking for a way to get something interpretable how a it is was a field
If you didn't already know, the Viewfield module allows you to specify a View as a field in your content type. This may save you some coding but you may not want a whole module to do such a specific task so...
If you want to continue with the custom code which you've written, then you need to re-structure your added content to the correct render array structure that Drupal expects. Try something like this:
$subprojects_view_output = array(
'#type' => 'markup',
'#markup' => $preview,
);
$subprojects = array(
'#theme' => 'field',
'#weight' => 10,
'#title' => t('Subprojects'),
'#items' => $subprojects_view_output,
);
With the above, the title/label for your field as well as the content of the field (the view itself) should show up. The code is untested so may not be 100% correct in terms of syntax and all but hopefully gives you a path to a solution.
EDIT: I tested the above and it does not work because in order to use the existing theme_field function it seems that Drupal expects more information required to render a field like the #field_name, #field_type, #entity_type, etc. as you should see in the warning messages.
Essentially, you are faking a field and you will need to provide Drupal with all the info it expects if you want to continue to use the built-in theme_field function, including all the variables as expected in the preprocess functions.
Alternatively, you can continue to use your original code and add a #prefix to get your title/label to render like this:
$subprojects = array(
'#weight' => 10,
'#prefix' => '<div id="subprojects-view">asdf:</div>',
//'#theme' => 'field',
'#markup' => $preview,
);
Then style the title/label with CSS accordingly. I didn't have any problems with the weighting as you described.
Thanks to #nmc, this is my final solution. If no results are found, it does not display the title. The check for results is done by if (count($view->result) == 0). Weight is working.
<?php
function projects_preprocess_node(&$variables) {
$type = $variables['type'];
if ($type == 'project' || $type == 'customer') {
if (isset($variables['view_mode']) && $variables['view_mode'] == 'full') {
_projects_add_subprojects_markup($variables);
}
}
}
function _projects_add_subprojects_markup(&$variables) {
$nid = $variables['nid'];
$view = views_get_view('projects');
$preview = $view->preview('subprojects', array($nid));
if (count($view->result) == 0) {
return;
}
$variables['content']['subprojects'] = array(
'#weight' => 10,
'#prefix' => '<h2>' . t('Subprojects') . '</h2>',
'#markup' => $preview,
);
}

Categories