Generating a dynamically expandable form with multiple similar categories
Answering your questions:


  Is this fine, as long as I'm getting the right output?


Not really, by your own admission, the possibility of more fields means you'll have blocks and blocks and blocks of the same code. See Structure below.


  I'm concerned about the repetition of code. Is there any way to improve this code if this is a bad practice of coding?


See The code below.



Structure:

Although the code given could be greatly simplified, I think to a degree your methodology is at fault.

Currently, you would have seven variables for each dynamic field, as the number (currently five) increases, the amount of localised variables would be out of control!

Rather than having such a large number of these, consider using a dictionary.

For example:

$content = [];
$content['featured_artist'] = $variable;


Additionally, considering the DRY (don't repeat yourself) principles, the count(array_filter($_FILES[ statements should be turned into a function, and returned to a variable, which can be inserted into the theoretical $content described above.



The code:

Using the following code as a base, with some re-structuring, and maybe some good old fashioned wizardry, the code can be reborn!


if (count(array_filter($_FILES['featured_artist']['name'])) &gt; 0) {

    $featured_artist_status= true;

    $featured_artist= $_FILES['featured_artist'];
    $featured_artist['tmp_name']['image'] = true;
    $featured_artist_field= Field::incrementFieldComparison($attraction['tmp_name'], $request-&gt;post('featured_artist_name'), $request-&gt;post('featured_artist_description'));

    $featured_artist_name = implode('|', $featured_artist_field-&gt;field_1);
    $featured_artist_description = implode('|', $featured_artist_field-&gt;field_2);
    $featured_artist_image = implode('|', $featured_artist_field-&gt;reference);
}



Such wizardry may look like this:

$completeFieldDict = [];
foreach ($_FILES as $key =&gt; $value){
    if (count(array_filter($value["name"])) &gt; 0){
        $completeFieldDict[$key] = buildField($key);
    }
}
function buildField($name){

    $fieldDictionary = [];
    $fieldDictionary['status'] = true;

    $fieldDictionary['content'] = $_FILES[$name];
    $fieldDictionary['content']['tmp_name']['image'] = true;
    $fieldDictionary['field'] = Field::incrementFieldComparison($fieldDictionary['content']['tmp_name'], $request-&gt;post($key . '_name'), $request-&gt;post($key . '_description'));

    $fieldDictionary['name']        = implode('|', $fieldDictionary['field']-&gt;field_1);
    $fieldDictionary['description'] = implode('|', $fieldDictionary['field']-&gt;field_2);
    $fieldDictionary['image']       = implode('|', $fieldDictionary['field']-&gt;reference);

    return $fieldDictionary;
}


Although, the limitations of this are notable, in form of such a large dictionary.
But, as previously mentioned this could be greatly improved upon refactoring the structure.

Personally, I'd suggest having a database transaction embedded within buildField(), and not saving variables locally.