Yii Rest. Failed to set unsafe attribute

119
22 января 2022, 13:20

Я пишу приложение - backend: Yii2, frontend : angular. Я использую Yii ActiveController. У меня уже реализована часть приложения, которая отвечает за выборку записей из базы данных и вывод. Теперь я задался вопросом редактирования данных и мне необходимо реализовать update записи. Это я делаю put запросом - но у меня возникла проблема - вижу в консоли отправляются мои данные - но мне приходит ответ - json - с первоначальными (неизменными) данными - хотя ответ от сервера 200 - я вижу в консоли следующую ошибку, мне пишет, что какой-то из атрибутов не является безопасным:

yii\base\Model::onUnsafeAttribute   Failed to set unsafe attribute '{"id":"f61e8b51-d16c-464d-9929-44d1b05dba10","lastname":"1Надь","name":"Виктор","fathername":"Сергеевич","location":5,"current_status":3,"company":"BADOO","position":"Middle_JS_Dev_Inc","salary":700,"date_birth":"2019-12-25","date_created":"2019-12-25_13:03:14","photo":null,"wish":null,"documents":null,"notes":null}' in 'app\crm\entities\candidate\Candidate'.

Моя entity Candidates(кандидаты):

<?php
/**
 * Created by PhpStorm.
 * User: Rox
 * Date: 28.03.2019
 * Time: 15:31
 */
namespace app\crm\entities\candidate;

use common\models\File;
use app\crm\entities\_traits\InstantiateTrait;
use app\crm\entities\_traits\LazyLoadTrait;
use app\crm\entities\candidate\related\Skills;
use app\crm\entities\City;
use app\crm\entities\Contact;
use app\crm\interfaces\IARCandidate;
use app\crm\interfaces\ICandidate;
use app\crm\interfaces\IEntity;
use app\crm\interfaces\ISkillEntity;
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior;
use ProxyManager\Proxy\LazyLoadingInterface;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\helpers\Json;
/***
 * Class Candidate
 * @package app\crm\entities\candidate
 *
 * @property ContactCandidate[] $relatedContacts
 * @property SkillCandidate[] $relatedSkills
 * @property DocumentCandidate[] $relatedDocuments
 * @property City $city
 */
class Candidate extends ActiveRecord implements IEntity, ICandidate, IARCandidate, ISkillEntity
{
    use LazyLoadTrait;
    /***@var $id CandidateId***/
    private $id;
    /***@var $name Name***/
    private $name;
    /***@var $createDate \DateTimeImmutable***/
    private $createDate;
    /***@var $birthDate \DateTimeImmutable***/
    private $birthDate;
    private $current_status;
    private $info;
    private $notes;
    private $documents;
    /***@var Contacts***/
    private $contacts;
    /***@var Skills***/
    private $skills;
    private $stages;
    private $photo;

    public static function create(CandidateId $id, Name $name, Info $info, string $photo,string $notes, int $location_id = null, array $contacts=[], array $skills=[], array $documents = []): self
    {
        $candidate = new static();
        $candidate->id              = $id;
        $candidate->name            = $name;
        $candidate->createDate      = new \DateTimeImmutable();
        $candidate->current_status  = Status::CREATED;
        $candidate->info            = $info;
        $candidate->contacts        = new Contacts($contacts);
        $candidate->skills          = new Skills($skills);
        $candidate->location        = $location_id;
        $candidate->photo           = $photo;
        $candidate->notes           = $notes;
        $candidate->documents       = new Documents($documents);
        return $candidate;
    }
    public function rename(Name $name): void
    {
        $this->name = $name;
        //$this->recordEvent(new Events\CandidateRenamed($this->id, $name));
    }
    public function remove(): void
    {
        if(false){
            throw new \DomainException('Some reason why can not delete candidate');
        }
        //$this->recordEvent(new Events\CandidateRemoved($this->id));
    }
    public function getId(): CandidateId
    {
        return $this->id;
    }
    public function getName(): Name
    {
        return $this->name;
    }
    public function getCurrentStatus(): int
    {
        return $this->current_status;
    }
    public function getCreateDate(): \DateTimeImmutable
    {
        return $this->createDate;
    }
    public function getBirthDate(): \DateTimeImmutable
    {
        return $this->info->getBirth();
    }
    public function getInfo(): Info
    {
        return $this->info;
    }
    public function setInfo(Info $info):void
    {
        $this->info = $info;
    }
    public function setLocation($location)
    {
        $this->location = $location;
    }
    public function getPhoto()
    {
       return $this->photo;
    }
    public function setPhoto($photo)
    {
        $this->photo = $photo;
    }
    public function getNotes()
    {
        return $this->notes;
    }
    public function setNotes($notes)
    {
        $this->notes = $notes;
    }
    public function getPosition(): string
    {
        return $this->info->getCurrentPosition();
    }
    public function getCompany(): string
    {
        return $this->info->getCurrentCompany();
    }
    public function getSalary(): int
    {
        return $this->info->getSalary();
    }
    public function setCurrentContacts(Contacts $contacts):void
    {
        $this->contacts = $contacts;
    }
    public function getCurrentContacts(): Contacts
    {
        return $this->contacts;
    }
    public function getContacts(): array
    {
        return $this->contacts->getAll();
    }
    public function getSkills(): array
    {
        return $this->skills->getAll();
    }
    public function getDocuments(): array
    {
        return $this->documents->getAll();
    }
    public function addDocument(File $file): array
    {
        $this->documents[] = $file;
        return $this->documents;
    }
    public function setCurrentDocuments(Documents $documents):void
    {
        $this->documents = $documents;
    }
    public function getCurrentDocuments(): Documents
    {
        return $this->documents;
    }
    /**** ******************************************** ***/
    public function getCurrentSkills(): Skills
    {
        return clone $this->skills;
    }
    public function setCurrentSkills(Skills $skills)
    {
        $this->skills = $skills;
    }
    /*** OLD METHOD **/
    private function setSkills(array $skills=[])
    {
        if(!empty($skills)) {
            $new_skills=[];
            /**создадим скиллы из массива*/
            foreach ($skills as $skill){
                $new_skills[]=new SkillCandidate((int)$skill);
            }
            /**удалим скилл если его нету в новом массиве*/
            foreach ($this->skills->getAll() as $key => $old_skill) {
                if (!in_array($old_skill->skill_id, $skills)) {
                    $this->skills->remove($key);
                }
            }
            /**добавим скилл */
            foreach ($new_skills as $new_skill) {
                try {
                    $this->skills->add($new_skill);
                }
                catch (\DomainException $de) {
                    //TODO:
                }
            }
        } else {
            /**удалим все*/
            foreach ($this->skills->getAll()  as $key=>$skill) {
                $this->skills->remove($key);
            }
        }
    }
    public function getSkillsToString(): string
    {
        $data = '';
        foreach ($this->getSkills() as $skill) {
            $data.= "<span class='skill'>{$skill->skill->name}</span>";
        }
        return $data;
    }
    private function prepareContacts(): array
    {
        $data = [];
        foreach ($this->getContacts() as $key=>$value) {
            $data[$value->type->name.$key] = $value->value;
        }
        return $data;
    }
    public function export(): array
    {
        $data = [
            'salary'    => $this->getSalary(),
            'location'  => $this->city->name??null,
        ];
        $contacts = $this->prepareContacts();
        $data = array_merge($data,$contacts);
        $data['skills']= $this->getSkillsToString();
        return $data;
    }
    public function getType(): string
    {
        return "candidate";
    }
    /**
     * @return array
     */
    public function releaseEvents(): array
    {
        // TODO: Implement releaseEvents() method.
        return [];
    }

    /***##SERVICE METHODS **/
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%candidates}}';
    }
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            [
                'class' =>  SaveRelationsBehavior::className(),
                'relations' => ['relatedContacts','relatedSkills','relatedDocuments'],
            ],
        ];
    }
    public function transactions(): array
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }
    public function afterFind(): void
    {
        $this->id = new CandidateId(
            $this->getAttribute('id')
        );
        $this->name = new Name(
            $this->getAttribute('lastname'),
            $this->getAttribute('name'),
            $this->getAttribute('fathername')
        );
        $this->info = new Info(
            $this->getAttribute('salary'),
            $this->getAttribute('company'),
            $this->getAttribute('position'),
            new \DateTimeImmutable($this->getAttribute('date_birth'))
        );
        $this->createDate = new \DateTimeImmutable(
            $this->getAttribute('date_created')
        );
        $this->photo = $this->getAttribute('photo');
        $this->notes = $this->getAttribute('notes');
        /*$this->birthDate = new \DateTimeImmutable(
            $this->getAttribute('date_birth')
        );*/
        $this->current_status = $this->getAttribute('current_status');
        $this->contacts = self::getLazyFactory()->createProxy(
            Contacts::class,
            function(&$target, LazyLoadingInterface $proxy) {
                $target = new Contacts($this->relatedContacts);
                $proxy->setProxyInitializer(null);
            }
        );
        $this->skills = self::getLazyFactory()->createProxy(
            Skills::class,
            function(&$target, LazyLoadingInterface $proxy) {
                $target = new Skills($this->relatedSkills);
                $proxy->setProxyInitializer(null);
            }
        );
//        $documents = $this->getAttribute('documents');
        $this->documents = self::getLazyFactory()->createProxy(
            Documents::class,
            function(&$target, LazyLoadingInterface $proxy) {
                $target = new Documents($this->relatedDocuments);
                $proxy->setProxyInitializer(null);
            }
        );

//        if(is_array($documents)) {
//            $this->documents = File::find()->where(['id' => $documents])->all();
//        } else {
//          $this->documents = [];
//        }
        parent::afterFind();
    }
    public function beforeSave($insert): bool
    {
        $this->setAttribute('id',           $this->id->getId() );
        $this->setAttribute('lastname',     $this->name->getLast() );
        $this->setAttribute('name',         $this->name->getFirst() );
        $this->setAttribute('fathername',   $this->name->getFather() );
        $this->setAttribute('current_status',$this->getCurrentStatus() );
        $this->setAttribute('date_created', $this->getCreateDate()->format('Y-m-d H:i:s') );
        $this->setAttribute('date_birth',   $this->getBirthDate()->format('Y-m-d') );
        $this->setAttribute('salary',       $this->info->getSalary());
        $this->setAttribute('company',      $this->info->getCurrentCompany() );
        $this->setAttribute('position',     $this->info->getCurrentPosition() );
        $this->setAttribute('photo',        $this->getPhoto());
        $this->setAttribute('notes',        $this->getNotes());
/*
        if(isset($this->documents)){
            $documents = [];
            foreach($this->getDocuments() as $document) {
//                $documents[] = $document->id;
                $documents[] = $document;
            }
            $this->setAttribute('documents', $documents);
        }
*/
        if (!$this->documents instanceOf LazyLoadingInterface || $this->documents->isProxyInitialized()) {
            $this->relatedDocuments = $this->documents->getAll();
        }
        if (!$this->contacts instanceOf LazyLoadingInterface || $this->contacts->isProxyInitialized()) {
            $this->relatedContacts = $this->contacts->getAll();
        }
        if (!$this->skills instanceOf LazyLoadingInterface || $this->skills->isProxyInitialized()) {
            $this->relatedSkills = $this->skills->getAll();
        }
        // TODO: remove these comments
//        $lastInsertedId =  \Yii::$app->db->createCommand('SELECT MAX(id) FROM files_uploaded')->queryScalar();
//
//        $model = \common\models\File::findOne(['id' => $lastInsertedId]);
//        $model->entity_id = $this->getId()->getId();
//        $model->save();

        return parent::beforeSave($insert);
    }
    /***RELATIONS***/
    public function getRelatedContacts(): ActiveQuery
    {
        return $this->hasMany(ContactCandidate::className(), ['candidate_id'=>'id'])->orderBy('contact_type');
    }
    public function getRelatedSkills(): ActiveQuery
    {
        return $this->hasMany(SkillCandidate::className(), ['candidate_id' => 'id'])->orderBy('skill_id');
    }
    public function getRelatedDocuments(): ActiveQuery
    {
        return $this->hasMany(DocumentCandidate::className(), ['entity_id' => 'id'])->orderBy('entity_id');
    }
    public function getCity()
    {
        return $this->hasOne(City::className(),['id'=>'location']);
    }
}

Я попытался задать в ней правила следующим образом:

    public function scenarios()
    {
        return [
            self::SCENARIO_DEFAULT => ['id','name','lastname','fathername','location','current_status','company','position','salary','date_birth','date_created','photo','wish','documents','notes'],
        ];
    }
    public function rules()
    {
        return [
            [['id','name','lastname','fathername','location','current_status','company','position','salary','date_birth','date_created','photo','wish','documents','notes'], 'safe','on' => 'update'],
        ];
    }

Все равно не могу избавиться от ошибки и соответственно данные не обновляются. Буду признателен за любые ответы!

Answer 1

У тебя - какой шаблон - basic? А попробуй-ка подключить yii\web\JsonParser:

    'components' => [ 
        'request' => [ 
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 
            'cookieValidationKey' => 'KkDF5NnbNMGCSH96cwn3ROrgil-6Z36N', 
            'csrfParam' => '_csrf-frontend', 
            'parsers' => [ 
                'application/json' => 'yii\web\JsonParser', 
            ], 
        ],

READ ALSO
Как сделать проверку на значение таблицы

Как сделать проверку на значение таблицы

Нужно чтобы шла проверка по всем пользователям в таблице (ban_drivers) по колонке (ban_do) если значение в (ban_do) у какого-то пользователя совпадает...

108
Разрешения для папки сессий

Разрешения для папки сессий

Я использую PHP на nginxКакие права должны быть установлена для папки сессий? Сейчас права 777 - это позволяет создавать и записывать сессии, но мне...

84
Фильтр товаров в интернет магазине

Фильтр товаров в интернет магазине

Ребят, в бд есть таблица с названием фильтров - sorting (поля: id, title), с пунктами фильтров - options (поля: id, sorting_id - id фильтра, title), таблица с товарами...

79
выводятся только первые строки таблицы

выводятся только первые строки таблицы

в БД есть таблица с данными продуктов из которых для каждого продукта должны выводиться определённые данныеНо в продуктах все данные повторяются

91