执行Job后Laravel每分钟报Call to undefined method错误排查
getName()方法 问题详情
我编写了OdataSyncStoragesService类用于从API拉取数据并存入数据库,同时有一个命令类负责创建Job并分配到队列,Job会调用该Service的sync()方法。执行命令后Job能正常运行,但laravel.log中每分钟都会出现以下错误:
production.ERROR: Call to undefined method App\Services\Odata\OdataSyncStoragesService::getName() {"exception":"[object] (Error(code: 0): Call to undefined method App\\Services\\Odata\\OdataSyncStoragesService::getName() at /var/www/fastuser/data/www/89.191.229.227/vendor/laravel/framework/src/Illuminate/Console/Scheduling/Schedule.php:138)
执行schedule:list命令时也会触发相同错误。项目中已有多个同模板的OdataSync****Service类,均能正常运行,不清楚为何该类会每分钟报错一次。
相关代码
OdataSyncStoragesService.php代码:
<?php namespace App\Services\Odata; use App\Models\OdataStorage; use GuzzleHttp\Exception\GuzzleException; use Throwable; class OdataSyncStoragesService extends OdataBaseService { const URL_STORAGES = "Catalog_Склады"; /** * Синхронизация атрибутов * * @return void * @throws GuzzleException * @throws Throwable */ public function sync(): void { $storages = $this->fetchStorages(); foreach ($storages as $storage) { $parsedStorage = $this->parseStorage($storage); OdataStorage::updateOrCreate([ 'Ref_Key' => $parsedStorage['Ref_Key'] ],$parsedStorage); } } private function parseStorage(array $storage) { return [ 'Ref_Key' => $storage['Ref_Key'], 'DataVersion' => $storage['DataVersion'], 'DeletionMark' => $storage['DeletionMark'], 'Parent_Key' => $storage['Parent_Key'], 'IsFolder' => $storage['IsFolder'], 'Description' => $storage['Description'], 'groupSelection' => $storage['ВыборГруппы'], 'useAddressStorage' => $storage['ИспользоватьАдресноеХранение'], 'useReferenceAddressStorage' => $storage['ИспользоватьАдресноеХранениеСправочно'], 'useOrderSchemeOnShipment' => $storage['ИспользоватьОрдернуюСхемуПриОтгрузке'], 'useOrderSchemeOnSurplusShortageReflection' => $storage['ИспользоватьОрдернуюСхемуПриОтраженииИзлишковНедостач'], 'useOrderSchemeOnReceipt' => $storage['ИспользоватьОрдернуюСхемуПриПоступлении'], 'useItemSeries' => $storage['ИспользоватьСерииНоменклатуры'], 'useWarehouseRooms' => $storage['ИспользоватьСкладскиеПомещения'], 'calendar_Key' => $storage['Календарь_Key'], 'controlOperationalBalances' => $storage['КонтролироватьОперативныеОстатки'], 'addressStorageSettings' => $storage['НастройкаАдресногоХранения'], 'department_Key' => $storage['Подразделение_Key'], 'businessRegion_Key' => $storage['БизнесРегион_Key'], 'retailPriceType_Key' => $storage['РозничныйВидЦены_Key'], 'currentResponsiblePosition' => $storage['ТекущаяДолжностьОтветственного'], 'currentResponsible_Key' => $storage['ТекущийОтветственный_Key'], 'warehouseType' => $storage['ТипСклада'], 'serviceLevel' => $storage['УровеньОбслуживания'], 'priceType_Key' => $storage['УчетныйВидЦены_Key'], 'startShipmentAfterTransportTaskCreation' => $storage['НачинатьОтгрузкуПослеФормированияЗаданияНаПеревозку'], 'useWorkAreas' => $storage['ИспользованиеРабочихУчастков'], 'priceInformationSourceForPrinting' => $storage['ИсточникИнформацииОЦенахДляПечати'], 'useIssueOrderStatuses' => $storage['ИспользоватьСтатусыРасходныхОрдеров'], 'useReceiptOrderStatuses' => $storage['ИспользоватьСтатусыПриходныхОрдеров'], 'useGoodsRevaluationStatuses' => $storage['ИспользоватьСтатусыПересчетовТоваров'], 'orderSchemeStartDateOnShipment' => $storage['ДатаНачалаОрдернойСхемыПриОтгрузке'], 'orderSchemeStartDateOnReceipt' => $storage['ДатаНачалаОрдернойСхемыПриПоступлении'], 'orderSchemeStartDateOnSurplusShortageReflection' => $storage['ДатаНачалаОрдернойСхемыПриОтраженииИзлишковНедостач'], 'warehouseUsageStartDate' => $storage['ДатаНачалаИспользованияСкладскихПомещений'], 'inventoryStorageStartDate' => $storage['ДатаНачалаАдресногоХраненияОстатков'], 'considerCostBySeries' => $storage['УчитыватьСебестоимостьПоСериям'], 'controlFreeBalances' => $storage['КонтролироватьСвободныеОстатки'], 'responsibleStorage' => $storage['СкладОтветственногоХранения'], 'luggageHolderType' => $storage['ВидПоклажедержателя'], 'luggageHolder' => $storage['Поклажедержатель'], 'luggageHolderTypeEnum' => $storage['Поклажедержатель_Type'], 'storageResponsibilityPeriod' => $storage['СрокОтветственногоХранения'], 'responsibleStorageUntilClaim' => $storage['ОтветственноеХранениеДоВостребования'], 'specialMarks' => $storage['ОсобыеОтметки'], 'goodsStorageConditions' => $storage['УсловияХраненияТоваров'], 'workshopWarehouse' => $storage['ЦеховаяКладовая'], 'orderPickingSupportedInStore' => $storage['ВМагазинеПоддерживаетсяСборкаЗаказов'], 'deliveryByOwnCouriersSupportedInStore' => $storage['ВМагазинеПоддерживаетсяДоставкаСвоимиКурьерами'], 'orderPickingRealizationMethod' => $storage['СпособСозданияРеализацииПриСборкеЗаказов'], 'fiscalizationMethodOnDelivery' => $storage['СпособФискализацииПриДоставке'], 'orderPickingAndDeliveryPerformedByOneEmployee' => $storage['СборкаИДоставкаВыполняетсяОднимСотрудником'], 'couriersCanAssignOrdersToThemselves' => $storage['КурьерыМогутНазначатьСебеЗаказы'], 'pickersCanAssignOrdersToThemselves' => $storage['СборщикиМогутНазначатьСебеЗаказы'], 'orderDeliveryStandardPeriod' => $storage['НормативныйСрокДоставкиЗаказов'], 'couriersUsePaymentTerminals' => $storage['КурьерыИспользуютЭквайринговыеТерминалы'], 'couriersUseAutonomousCashRegisters' => $storage['КурьерыИспользуютАвтономныеККТ'], 'orderPickingStartDate' => $storage['ДатаНачалаСборкиЗаказов'], 'deliveryByOwnCouriersStartDate' => $storage['ДатаНачалаДоставкиСвоимиКурьерами'], 'goodsGrouping' => $storage['ГруппировкаТоваров'], 'individualPriceType_Key' => $storage['ИндивидуальныйВидЦены_Key'], 'contactInformation' => json_encode($storage['КонтактнаяИнформация']), 'additionalAttributes' => json_encode($storage['ДополнительныеРеквизиты']), 'predefined' => $storage['Predefined'], 'predefinedDataName' => $storage['PredefinedDataName'], 'parentNavigationLinkUrl' => $storage['Parent@navigationLinkUrl'] ?? '', 'departmentNavigationLinkUrl' => $storage['Подразделение@navigationLinkUrl'] ?? '', 'retailPriceTypeNavigationLinkUrl' => $storage['РозничныйВидЦены@navigationLinkUrl'] ?? '' ]; } /** * @param bool $onlyVersions * @return array|mixed * @throws GuzzleException */ private function fetchStorages(): mixed { $params = [ '$format' => 'json', ]; $result = $this->sendRequest(self::URL_STORAGES, 'GET', $params); if (!$result['success']) { return []; } return $result['data']['value'] ?? []; } }
原因分析
错误出现在Laravel的Schedule类中,说明你的OdataSyncStoragesService被误配置为计划任务的执行主体。Laravel计划任务系统会调用任务类的getName()方法来标识任务,但你的Service类并未实现该方法。每分钟报错是因为Laravel的schedule:run命令默认每分钟执行一次,执行时会加载所有已配置的计划任务并验证其结构。
解决方案
检查计划任务配置
打开app/Console/Kernel.php,找到schedule方法,确认是否错误地将OdataSyncStoragesService直接添加到计划任务中。正确的配置应该是调用对应的命令类,而非Service类:// 错误写法:直接引用Service类 // $schedule->call(OdataSyncStoragesService::class)->everyMinute(); // 正确写法:引用命令类 $schedule->command('odata:sync-storages')->everyMinute();对比正常Service类的结构
检查项目中其他正常运行的OdataSync****Service类,查看父类OdataBaseService是否实现了getName()方法,或者这些类是否混入了计划任务相关的Trait。如果父类有该方法,确保你的Service类正确继承了父类的所有方法。临时修复(若需保留错误配置)
如果因特殊需求必须将Service类作为计划任务执行,可以手动添加getName()方法:public function getName(): string { return 'odata.sync.storages'; // 返回自定义的任务名称 }
内容的提问来源于stack exchange,提问作者Eugene Nail

