Call to a member function load() on array

I’ve got a controller

 public function actionLegIndexNew($id){
	$modelLeg = ProjectsLegs::findOne(['LegId' => $id]);
	$modelInvoicesItems = $modelLeg->projectsLegsInvoicesItems;

	if ($modelInvoicesItems ->load(Yii::$app->request->post())) { //***Errs here***
	...

but this generates an “Exception ‘Error’ with message ‘Call to a member function load() on array’” error.

If I print_r(Yii::$app->request->post()), I get

Array
(
    [_csrf-backend] => ag2d20Umu5SFKS_ZNWT02NgRzul3oWvX5TNNDEzWh0gSbM25KFOL09FARY5cAdmZtnOQ0DDID7bXfH5tO6T0Bj==
    [ProjectsLegsInvoicesItems] => Array
        (
            [0] => Array
                (
                    [InvoiceItemId] => 31375
                    [PilotCarItemId] => 806
                    [Qty] => 529.00
                    [UnitPrice] => 1.70
                    [SubTotal] => 899.30
                    [TaxRateId] => 21
                )

            [1] => Array
                (
                    [InvoiceItemId] => 31376
                    [PilotCarItemId] => 796
                    [Qty] => 529.00
                    [UnitPrice] => 0.00
                    [SubTotal] => 0.00
                    [TaxRateId] => 21
                )

            [2] => Array
                (
                    [InvoiceItemId] => 31377
                    [PilotCarItemId] => 821
                    [Qty] => 1.00
                    [UnitPrice] => 90.00
                    [SubTotal] => 90.00
                    [TaxRateId] => 21
                )

        )

    [SubTotal] => 989.30
)

This is for a dynamic form and [ProjectsLegsInvoicesItems] [SubTotal] and [SubTotal] are not part of the model, should that make a difference. They are Html::input() added to display extra information.

$modelInvoicesItems is receiving an array, then you are trying to run the load method in it.

Consider using Model::loadMultiple()
https://www.yiiframework.com/doc/api/2.0/yii-base-model#loadMultiple()-detail

And the following section of the guide will be a help.
https://www.yiiframework.com/doc/guide/2.0/en/input-tabular-input

Sorry, I guess I should have supplied all my code, as I’m already using loadMultiple.

My controller is

public function actionLegIndexNew($id)
{
	$modelLeg = ProjectsLegs::findOne(['LegId' => $id]);
	$modelInvoicesItems = $modelLeg->projectsLegsInvoicesItems;

	if (Yii::$app->request->post('ProjectsLegsInvoicesItems')) {
		$oldItemIds = ArrayHelper::map($modelInvoicesItems, 'InvoiceItemId', 'InvoiceItemId');
		$modelInvoicesItems = Model::createMultipleProjectsLegsInvoicesItems(ProjectsLegsInvoicesItems::class, $modelInvoicesItems);
		Model::loadMultiple($modelInvoicesItems, Yii::$app->request->post('ProjectsLegsInvoicesItems'));
		$deletedItemsIds = array_diff($oldItemIds, array_filter(ArrayHelper::map($modelInvoicesItems, 'InvoiceItemId', 'InvoiceItemId')));

		$valid = Model::validateMultiple($modelInvoicesItems);
		if (!$valid) {
			$validationErrors = '';
			$iCounter = 0;
			foreach ($modelInvoicesItems as $modelsItem) {
				$iCounter++;
				$valid = $modelsItem->validate();
				if (!$valid) {
					if ($validationErrors != '') {
						$validationErrors .= '<br>';
					}
					$validationErrors .= '<b>Work Item Entry No. ' . $iCounter . '</b><br>';
					foreach ($modelsItem->getErrors() as $attribute => $errors) {
						$validationErrors .= '<div class="col-md-offset-1">' . $attribute . ' - ' . implode('<br>', $errors) . '</div>';
					}
				}
			}
			return json_encode([
				'status' => 'Error',
				'message' => '<i class="glyphicon glyphicon-remove"></i> There are problems with the following Invoice Items entries!',
				'errors' => $validationErrors
			]);
		}
		if ($valid) {
			$transaction = \Yii::$app->db->beginTransaction();
			try {
				$flag = true;
				// if ($flag = $modelLeg->save(false)) {
				if (!empty($deletedItemsIds)) {
					ProjectsLegsInvoicesItems::deleteAll(['InvoiceItemId' => $deletedItemsIds]);
				}
				foreach ($modelInvoicesItems as $modelsItem) {
					$modelsItem->LegId = $modelLeg->LegId;
					if (!($flag = $modelsItem->save(false))) {
						$transaction->rollBack();
						return json_encode(['status' => 'Error', 'message' => 'Item not created!']);
						break;
					}
				}
				// }
				if ($flag) {
					$transaction->commit();
					return json_encode(['status' => 'Success', 'message' => '<i class="glyphicon glyphicon-ok"></i> Entry updated successfully.']);
				}
			} catch (ErrorException $e) {
				$transaction->rollBack();
				return json_encode(['status' => 'Error', 'message' => 'Something went wrong!']);
			}
		}
	} else {
		return $this->renderAjax('indexn', [
			'modelLeg' => $modelLeg,
			'modelInvoicesItems' => (empty($modelInvoicesItems)) ? [new ProjectsLegsInvoicesItems] : $modelInvoicesItems,
		]);
	}
}

Model::createMultipleProjectsLegsInvoicesItems is

public static function createMultipleProjectsLegsInvoicesItems($modelClass, $multipleModels = [])
{
	$model    = new $modelClass;
	$formName = 'ProjectLegsInvoiceItems';
	$post     = Yii::$app->request->post($formName);
	$models   = [];

	if (! empty($multipleModels)) {
		$keys = array_keys(ArrayHelper::map($multipleModels, 'InvoiceItemId', 'InvoiceItemId'));
		$multipleModels = array_combine($keys, $multipleModels);
	}

	if ($post && is_array($post)) {
		foreach ($post as $i => $item) {
			if (isset($item['InvoiceItemId']) && !empty($item['InvoiceItemId']) && isset($multipleModels[$item['InvoiceItemId']])) {
				$models[] = $multipleModels[$item['InvoiceItemId']];
			} else {
				$models[] = new $modelClass;
			}
		}
	}

	unset($model, $formName, $post);

	return $models;
}

The View is

<?php
use yii\bootstrap\ActiveForm;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url; //used to make create a popup window

use wbraganca\dynamicform\DynamicFormWidget;

use backend\models\LstPilotCarItems;
use backend\models\LstTaxRates;
use backend\models\ProjectsLegsInvoicesItems;
/* @var $this yii\web\View */
/* @var $searchModel backend\models\ProjectsLegsInvoicesItemsSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = Yii::t('app', 'Projects Legs Invoices Items');
// $this->params['breadcrumbs'][] = $this->title;
?>
<div class="projects-legs-invoices-items-index">
<?php 
    $form = ActiveForm::begin([
        // 'id' => $modelLeg->formName(),
        'id' => 'ProjectLegsInvoiceItems',
        'fieldConfig' => [
            'enableError' => true,
            'options' => ['class' => ''],
            'template' => '<div class="col-md-2 no-padding">{label}</div><div class="col-md-10">{input}{error}</div>',
            'labelOptions' => [
                'class' => 'control-label'
            ],
        ],
        'layout' => 'horizontal', //'default', 'horizontal' or 'inline'
    ]); 
?>
<div class="modal-body">
<table>
<tbody>
    <tr>
        <th>Leg No: </th>
        <td><?= $modelLeg->LegNo ?></td>
    </tr>
    <tr>
        <th>From: </td>
        <td><?= $modelLeg->LocFrom ?></th>
    </tr>
    <tr>
        <th>To: </td>
        <td><?= $modelLeg->LocDest ?></th>
    </tr>
    <tr>
        <th>Type of Service:&nbsp;</th>
        <td><?= $modelLeg->lstProjectsLegsServtypes->ServType ?></td>
    </tr>
    <tr>
        <th>Rate: </th>
        <td><?= $modelLeg->EmpRate ?></td>
    </tr>
</tbody>
</table>
<br>
<?php
    DynamicFormWidget::begin([
        'widgetContainer' => 'projectsLegsInvoicesItems',
        'widgetBody' => '.items-items',
        'widgetItem' => '.item-item',
        'limit' => 25,
        'min' => 0,
        'insertButton' => '.add-item',
        'deleteButton' => '.remove-item',
        'model' => $modelInvoicesItems[0],
        // 'formId' => 'dynamic-form',
        'formId'=>$modelLeg->formName(),
        'formFields' => [
            // 'InvoiceItemId',
            'PilotCarItemId',
            'Qty',
            'UnitPrice',
            'TaxRateId',
        ],
    ]); 
?>
    <div class="panel panel-body">
            <div class="scrollyitems">
            <table id="table-items" class="table table-bordered table-condensed">
                <thead>
                    <tr class="active">
                        <td style="min-width: 25px;">&nbsp;</td>
                        <td style="min-width: 25px;"><label>No.</label></td>
                        <td style="min-width: 5px;"><?= Html::Label('Item'); ?></td>
                        <td style="min-width: 125px;"><?= Html::Label('Qty'); ?></td>
                        <td style="min-width: 125px;"><?= Html::Label('Unit Price'); ?></td>
                        <td style="min-width: 125px;"><?= Html::Label('Sub-Total'); ?></td>
                        <td style="display: none;"></td>
                    </tr>
                </thead>
                <tbody class="table-striped items-items"><!-- widgetContainer -->
                <?php foreach ($modelInvoicesItems as $i => $modelInvoicesItem): 
                    //$rowClass = ($modelInvoicesItem->Active != 1 ? ' danger' : ''); 
                    $rowClass =''; ?>
                    <tr class="item-item<?= $rowClass ?>"><!-- widgetBody -->
                        <td class="align-middle">
                            <button type="button" class="remove-item btn btn-danger btn-xs table-control" tabindex="-1"><i class="glyphicon glyphicon-minus"></i></button>
                            <?php
                            if (! $modelInvoicesItem->isNewRecord) {
                                echo Html::activeHiddenInput($modelInvoicesItem, "[{$i}]InvoiceItemId",['class' => 'table-control']);
                            }
                            ?>
                        </td>
                        <td class="align-middle text-right index"><?= $i+1 ?></td>
                        <td>
                            <?php
                                $query = LstPilotCarItems::find()
                                    ->distinct()
                                    ->innerJoin('clients_price_list', 'lst_pilot_car_items.PilotCarItemId = clients_price_list.PilotCarItemId')
                                    ->innerJoin('projects', 'projects.ClientId = clients_price_list.ClientId')
                                    ->rightJoin('clients', 'clients.ClientId = projects.ClientId')
                                    ->innerJoin('projects_legs', 'projects_legs.ProjId = projects.ProjId')
                                    ->where(["=", "projects_legs.LegId", $modelLeg->LegId])
                                    ->andWhere('lst_pilot_car_items.CurrencyCode = clients.CurrencyCode')
                                    ->andWhere('lst_pilot_car_items.CreditCardCompany = clients.CreditCardCompany')
                                    ->orderBy([
                                        'lst_pilot_car_items.SortOrder'=>SORT_ASC,
                                        'lst_pilot_car_items.PilotCarItem'=>SORT_ASC,
                                    ]);  

                                echo $form->field($modelInvoicesItem, "[{$i}]PilotCarItemId",['options' => ['class' => 'table-control']])->begin();
                                // echo Html::activeTextInput($modelInvoicesItem, "[{$i}]PilotCarItemId", ['class' => 'form-control']); //Field
                                echo Html::activeDropDownList($modelInvoicesItem, 
                                        "[{$i}]PilotCarItemId",
                                        ArrayHelper::map($query->all(),
                                            'PilotCarItemId',
                                            'PilotCarItem'
                                        ),
                                    [
                                        'class' => 'form-control',
                                        'prompt' => 'Select an Item ...',
                                    ]
                                );
                                echo Html::error($modelInvoicesItem,"[{$i}]PilotCarItemId", ['class' => 'help-block']); //error
                                echo $form->field($modelInvoicesItem, "[{$i}]PilotCarItemId")->end();
                            ?>
                        </td>
                        <td>
                            <?php
                                echo $form->field($modelInvoicesItem, "[{$i}]Qty",['options' => ['class' => 'table-control']])->begin();
                                echo Html::activeTextInput($modelInvoicesItem, "[{$i}]Qty", ['class' => 'form-control text-right', 'onchange' => 'recalcSubTotal(this);calculateSum();']); //Field
                                echo Html::error($modelInvoicesItem,"[{$i}]Qty", ['class' => 'help-block']); //error
                                echo $form->field($modelInvoicesItem, "[{$i}]Qty")->end();
                            ?>
                        </td>
                        <td>
                            <?php
                                echo $form->field($modelInvoicesItem, "[{$i}]UnitPrice",['options' => ['class' => 'table-control']])->begin();
                                echo Html::activeTextInput($modelInvoicesItem, "[{$i}]UnitPrice", ['class' => 'form-control text-right','onchange' => 'recalcSubTotal(this);calculateSum();','tabIndex' => -1,]); //Field
                                echo Html::error($modelInvoicesItem,"[{$i}]UnitPrice", ['class' => 'help-block']); //error
                                echo $form->field($modelInvoicesItem, "[{$i}]UnitPrice")->end();
                            ?>
                        </td>
                        <td>
                        <?php
                            echo Html::input('text',"ProjectsLegsInvoicesItems[$i][SubTotal]",'', 
                                [
                                    'class' => 'form-control text-right',
                                    'id' => "projectslegsinvoicesitems-$i-subtotal",
                                    'readonly' => true,
                                    'tabIndex' => -1,
                                ]);
                        ?>
                        </td>
                        <td style="display: none;">
                            <?php
                                echo $form->field($modelInvoicesItem, "[{$i}]TaxRateId",['options' => ['class' => 'table-control']])->begin();
                                echo Html::activeDropDownList($modelInvoicesItem, 
                                        "[{$i}]TaxRateId",
                                        ArrayHelper::map(
                                            LstTaxRates::find()
                                                ->FilterWhere(['like', 'Name', 'Sales'])
                                                ->orWhere(['TaxType'=>'NONE'])
                                                ->orderBy(['Name'=>SORT_ASC,])
                                                ->all(),
                                            'TaxRateId',
                                            function($model) {
                                                return $model['Name'].' ('.$model['EffectiveRate'].'%)';
                                            }
                                    ),
                                    [
                                        'class' => 'form-control',
                                        'prompt'=>'Select a Tax Rate ...',
                                        'tabIndex' => -1,
                                    ]
                                );
                                echo Html::error($modelInvoicesItem,"[{$i}]TaxRateId", ['class' => 'help-block']); //error
                                echo $form->field($modelInvoicesItem, "[{$i}]TaxRateId")->end();
                            ?>
                        </td>
                    </tr>
                <?php endforeach; // end of loads loop ?>
                </tbody>
                <tfoot>
                    <tr>
                        <td colspan="5"></td>
                        <td>
                        <?php
                            echo Html::input('text',"SubTotal",'', 
                            [
                                'class' => 'form-control text-right',
                                'id' => "subtotal",
                                'readonly' => true,
                                'tabIndex' => -1,
                            ]);
                        ?>
                        </td>
                    </tr>
                    <tr>
                        <td colspan="7" class="active">
                            <button type="button" class="btn btn-xs btn-success add-item"><i class="glyphicon glyphicon-plus"></i> Add New Item</button>
                        </td>
                    </tr>
                </tfoot>
            </table>
        </div>
    </div>
<?php 
    DynamicFormWidget::end();
?>
</div>
<button type="submit" id="btn-close" class="btn btn-block btn-success form-cmd" title="Save and close">
    <span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> Create</button>
<?php
    ActiveForm::end();
?>
</div>
<?php 
$script = <<< JS
$('form#ProjectLegsInvoiceItems').on('beforeSubmit', function(e){
// $('form#{$modelLeg->formName()}').on('beforeSubmit', function(e){
    'use strict';

    $("#loading").show();

    var \$form = $(this);
    $.post(
        \$form.attr("action"), //serialize Yii2 form 
        \$form.serialize()
    )
    .done(function(result){
        result = JSON.parse(result);
        var divClass = (result.status == 'Error')? 'danger' : result.status.toLowerCase();
        var sysMsg = '<div id="w0-' + result.class + '-0" class="alert-' + divClass + ' alert fade in">'
                + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
                + result.status + '<br>' + result.message + '<br>' + (result.errors === undefined?'':'<br><br>' + result.errors) + '</div>';

        if(result.status == 'Success'){
            $(\$form).trigger("reset");
            $('#modal-system-messages').html("");
            $('#modalPopup2 #modalHeaderTitle').html("");
            $('#modalPopup2 #modal-system-messages').html("");
            $('#modalPopup2 #modalContent').html("");
            $('#modalPopup2').modal('hide');
            $('#system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000).fadeOut(4000);
        }else{
            $('#modal-system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000);
        }
        $("#loading").fadeOut("slow");
    })
    .fail(function(xhr, status, error){
        var msg = "<span class=\"glyphicon glyphicon-remove\"></span><b>Javascript Error::Form/beforeSubmit()</b><br>"+xhr.responseText;
        var sysMsg = '<div id="w0-error-0" class="alert-danger alert fade in">'
                    + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
                    + '<span class="glyphicon glyphicon-warning-sign"></span> '+msg+'</div>';
        $('#modal-system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000);
        $("#loading").fadeOut("slow");
    });

    return false;
});
$(function(){
    calcSubTotals();
    calculateSum();

    $(".projectsLegsInvoicesItems").on("afterInsert", function(e, item) {
        $('#table-items > tbody > tr:last > td:nth-child(2)').html(item.rowIndex);
        $('#projectslegsinvoicesitems-' + (item.rowIndex - 1) + '-pilotcaritemid').focus();
    })

    if($('#projectslegsinvoicesitems-0-pilotcaritemid').length > 0){ $('#projectslegsinvoicesitems-0-pilotcaritemid').focus(); }
})
function recalcSubTotal(elem){
    var callingElem = $(elem);
    var Qty = callingElem.closest("tr").find("[id$='-qty']").val();
    var UnitPrice = callingElem.closest("tr").find("[id$='-unitprice']").val();
    var SubTotal = Qty * UnitPrice;
    callingElem.closest("tr").find("[id$='-subtotal']").val(SubTotal.toFixed(2));

    return false;
};
function calcSubTotals(){
    var noRows = $("#table-items > tbody > tr").length;
    for (var i = 0; i < noRows; i++) {
        recalcSubTotal($('#projectslegsinvoicesitems-' + i + '-unitprice'));
    }
}
function calculateSum(){
    var noRows = $("#table-items > tbody > tr").length; //Zero based!
    var subtotal = 0;
    for (var i = 0; i < noRows; i++) {
        value = $('#projectslegsinvoicesitems-' + i + '-subtotal').val();
        if (typeof (value) === 'string') {
            value = value.replace(/[$,]+/g,"");
        }
        value = parseFloat(value);
        subtotal = subtotal + value;
    }
    $('#subtotal').val(subtotal.toFixed(2));
}
JS;
$this->registerJS($script);
?>

For some reason I suspect my issue lies with the $post assignment in
Model::createMultipleProjectsLegsInvoicesItems, but could easily be wrong.

In this instance, $modelLeg is just for information purposes and does not need to be saved in any way, I don’t have a form with it, just use it to populate some reference details in a table.

OK. So what is the issue with your code?

I use a very similar technique in multiple other forms, yet this one does not work. I try and debug and my models return ‘1’? not an array? Truthfully, at this point I’m lost.

I’ve been working on this for two days trying all sorts of things, to the point now, that I’ve broken my view, it won’t load anymore.

In order to use Model::loadMultiple(), you need to make ready an array of target models. But you don’t have to fill them with valid values. They can be newly created empty ones. Only the size of the returned array matters.

I think that createMultipleProjectsLegsInvoicesItems can be as simple as this:

public static function createMultipleProjectsLegsInvoicesItems($modelClass) 
{
    $formName = 'ProjectLegsInvoiceItems';
    $models = [];
    for ($i = 0; $i < count(Yii::$app->request->post('ProjectLegsInvoiceItems', [])); $i++) {
        $models[] = new $modelClass;
    }
    return $models;
}

Thank you.

I will try it out once I figure out what I’ve done to cripple my view file, it won’t submit anymore?! No errors, just doesn’t submit. I’ll eventually figure it out and post back.

$modelInvoicesItems should be a model object, seems like it’s an array of objects

I’m following https://github.com/wbraganca/yii2-dynamicform the difference in this case is that parent model is not important here, doesn’t need to be saved in any way, only the child models are what I’m trying to manage.

When debugging

$oldItemIds = ArrayHelper::map($modelInvoicesItems, 'InvoiceItemId', 'InvoiceItemId');

returns correctly

Model::createMultipleProjectsLegsInvoicesItems(ProjectsLegsInvoicesItems::class, $modelInvoicesItems);

returns all null values for new entries?

Model::loadMultiple($modelInvoicesItems, Yii::$app->request->post('ProjectsLegsInvoicesItems'));

seems to be identical and never seems to populate the new entries, so the model never validates properly.

Finally figured it out after consulting the documentation https://www.yiiframework.com/doc/api/2.0/yii-base-model#loadMultiple()-detail

I changed

Model::loadMultiple($modelInvoicesItems, Yii::$app->request->post('ProjectsLegsInvoicesItems'));

to

Model::loadMultiple($modelInvoicesItems, Yii::$app->request->post(), 'ProjectsLegsInvoicesItems');

and everything is in order again.

Thank you all for trying to help!

2 Likes