Yii Rest. Failed to set unsafe attribute

Всё что касается построения API
Ответить
grpetr189853
Сообщения: 11
Зарегистрирован: 2019.03.27, 13:27
Откуда: Украина, г.Полтава
Контактная информация:

Yii Rest. Failed to set unsafe attribute

Сообщение grpetr189853 »

Я пишу приложение - 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(кандидаты):

Код: Выделить всё

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'],
        ];
    }
Все равно не могу избавиться от ошибки и соответственно данные не обновляются. Буду признателен за любые ответы!
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Yii Rest. Failed to set unsafe attribute

Сообщение yiijeka »

сценарий update не забыли указать когда сохраняете?
grpetr189853
Сообщения: 11
Зарегистрирован: 2019.03.27, 13:27
Откуда: Украина, г.Полтава
Контактная информация:

Re: Yii Rest. Failed to set unsafe attribute

Сообщение grpetr189853 »

Спасибо - за ответ - сегодня посмотрю - и отпишусь.....!!!
grpetr189853
Сообщения: 11
Зарегистрирован: 2019.03.27, 13:27
Откуда: Украина, г.Полтава
Контактная информация:

Re: Yii Rest. Failed to set unsafe attribute

Сообщение grpetr189853 »

Такой у меня вопрос появился - как в ActiveController - укзазать сценарии - я добавил строчку в контроллер:
public $updateScenario = Candidate::SCENARIO_UPDATE;
И в entity:
const SCENARIO_UPDATE = 'update';
[['id','name','lastname','fathername','location','current_status','company','position','salary','date_birth','date_created','photo','wish','documents','notes'], 'safe','on' => self::SCENARIO_UPDATE]

Как теперь указать контроллеру, что бы он использовал этот сценарий - при update модели?
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: Yii Rest. Failed to set unsafe attribute

Сообщение yiiliveext »

Так

Код: Выделить всё

$updateScenario =  Candidate::SCENARIO_UPDATE;
Или так

Код: Выделить всё

public function actions()
    {
        $actions = parent::actions();
        $actions['update']['scenario'] = Candidate::SCENARIO_UPDATE;
        return $actions;
    }
grpetr189853
Сообщения: 11
Зарегистрирован: 2019.03.27, 13:27
Откуда: Украина, г.Полтава
Контактная информация:

Re: Yii Rest. Failed to set unsafe attribute

Сообщение grpetr189853 »

Спасибо!!!Всех с наступающим Новым Годом!!!
Ответить