Php: Self-Referencing Array

PHP: Self-referencing array

The answer to this, as it turns out, is Yes. However it is not a tidy syntax as it uses a sort of sub-statement, and leaves the current scope littered with an extra reference variable.

Consider the following code:

<?php

$array = array(

// Creates Key1 and assigns the value to it
// A copy of the value is also placed in $ref
// At this stage, it's not a reference
"Key1"=>($ref = array(
"Value1",
"Value2"
)),

// Now Key2 is a reference to $ref, but not to Key1
"Key2"=>&$ref,

// Now everything is referenced together
"Key1"=>&$ref

);

I was surprised that this worked with no errors, but it does - here's the proof. Of course, you wouldn't do this, but you can...

PHP Self-referencing script

You must verify that you was send the page and $_POST exist. And correct the select element

<form action = "<?php $_SERVER['PHP_SELF'] ?>" method = "post">
<input type = "number" id = "temp2" name = "temperature2" placeholder = "28">
<label for = "temp2"> degrees </label>

<select name = "conv">
<option value = "f"> Fahrenheit </option>
<option value = "c"> Celsius </option>
</select>

<input type = "submit" value = "equals">

<?php

if(isset($_POST["temperature2"])) {

$type = $_POST["conv"];
$tmp = $_POST["temperature2"];
if ($type == "f") {
$newTmp = (9/5 * $tmp) + 32;
echo $newTmp . " degrees Celsius.";
}
elseif ($type == "c") {
$newTmp = (5 * ($tmp - 32)) / 9;
echo $newTmp . " degrees Fahrenheit.";
}
}
?>

</form>

self-referencing table and arrays

Your table structure is fine.

You'll render the nesting when you deal with the results; perhaps you loop through each result with no parent and append to the DOM, then loop through each result with a parent appending to existing elements. That only works for a two-level tree, but you get the idea.

Create Nested Array From Database Self Referencing Table And Show in Nested list In Smarty Template

You can use recursive function like this:

function addToArr(&$arr, $data) {
if ($data["parent_id"] == 0)
return $arr[] = ["id" => $data["id"], "name" => $data["name"], "children"=> []];
foreach($arr as &$e) {
if ($e["id"] == $data["parent_id"]) { // if found add as child
$e["children"][] = ["id" => $data["id"], "name" => $data["name"], "children"=> []];
break;
}
addToArr($e["children"], $data); // call recursively
}
}

Live example: 3v4l

Edit

To have HTML tag:

function getHTMLformat($arr) {
if (!$arr) return "";
$str = "<ul>" . PHP_EOL; // starting the list
foreach ($arr as $e) {
$str .= "<li>" . $e["name"] . getHTMLformat($e["children"]) . "</li>" . PHP_EOL;
}
return $str . "</ul>" . PHP_EOL; // end list
}

echo getHTMLformat($res);

Reference key from same array

You can use the fact that assignment is itself an expression in PHP:

$glossary_args = array(
'name' => ($name = 'Glossary Terms'),
'singular_name' => 'Glossary Term',
'add_new' => 'Add New Term',
'edit_item' => 'Edit Term',
'search_items' => 'Search'.$name
)

Form with self-referencing data (Symfony 5)

The answer from Dylan Kas was good, just by adding a new form, it's good.

The Person Form

class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['attr' => ['class' => 'form_textfield']])
->add('firstname')
->add('birthdayDate', TextType::class, ['attr' => ['class' => 'form_datetime']])
->add('gender', GenderType::class)
->add('submit', SubmitType::class)
->add('myFamily', CollectionType::class, array('entry_type' => ChildType::class, 'by_reference' => false, 'allow_add' => true, 'allow_delete' => true));
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}

The child, referenced by myFamily :

class ChildType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['attr' => ['class' => 'form_textfield']])
->add('firstname')
->add('birthdayDate', TextType::class, ['attr' => ['class' => 'form_datetime']])
->add('gender', GenderType::class)
->add('submit', SubmitType::class);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}

And the view :

{% block body %}
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.firstname) }}
{{ form_row(form.birthdayDate) }}
{{ form_row(form.gender) }}
<button type="button" class="add_item_link" data-collection-holder-class="myFamily">Add a tag</button>
<ul class="myFamily" data-index="{{ form.myFamily|length > 0 ? form.myFamily|last.vars.name + 1 : 0 }}" data-prototype="{{ form_widget(form.myFamily.vars.prototype)|e('html_attr') }}"></ul>
{{ form_end(form) }}
{% endblock %}

With the js associated

const addFormToCollection = (e) => {
const collectionHolder = document.querySelector(
"." + e.currentTarget.dataset.collectionHolderClass
);

const item = document.createElement("li");

item.innerHTML = collectionHolder.dataset.prototype.replace(
/__name__/g,
collectionHolder.dataset.index
);

collectionHolder.appendChild(item);

collectionHolder.dataset.index++;
};

document
.querySelectorAll(".add_item_link")
.forEach((btn) => btn.addEventListener("click", addFormToCollection));

It still need some work, maybe I can make the child form extending the person form. The front need some work too. But the next people facing this problem will have the solution here.

I am still asking myself how could I do if I've needed to have a form including itself the same form, including itself the same form etc...
The form would be recursivable.

PHP function pass by reference self::$menus

You are passing it in as a recursive call here:

self::addChild($item_id, $title, $url, $parent_id, $value);

Which might be better as:

static::addChild($item_id, $title, $url, $parent_id, $value);

So just use static::$menus instead of $array if nothing was passed in:

public static function addChild($item_id, $title, $url, $parent_id, &$array=null)
{
if($array === null) {
$array = &static::$menus;
}
// other code
}

Or this might be better since you actually need an array:

if(!is_array($array)) {
$array = &static::$menus;
}

Then for the main calls (not recursive) just omit the $array parameter.

  • What is the difference between self::$bar and static::$bar in PHP?
  • PHP Manual - Late Static Bindings

Is it possible for a variable to reference itself

So, thanks to RandomSeed reminding me of variable references, I came up with what I think is the closest solution I'm likely to get.

Take this as an example:

   $array['index1']['index2']['index3']['index4'];
$array['index1']['index2']['index3']['index4'] = array_merge(
$array['index1']['index2']['index3']['index4'],
array('test' => 'answer'),
array('test2' => 'answer'));

This is unappealing in my eyes, and a bit difficult to follow, especially if there are multiple of these on a page, or in a controller action.

So this is an alternative:

   $self = &$array['index1']['index2']['index3']['index4'];
$array['index1']['index2']['index3']['index4'] = array_merge(
$self,
array('test' => 'answer'),
array('test2' => 'answer'));

A Quick test on my live environment seems to verify that it works.



Related Topics



Leave a reply



Submit