I have override actionIndex() method. Now i want to use expand in param. My code is here
public function actions() {
$actions = parent::actions();
unset($actions['index']);
unset($actions['create']);
unset($actions['update']);
unset($actions['delete']);
return $actions;
}
public function actionIndex() {
$query = $this->model::find();
$query->someCondition()->all();
return $query
}
My Model is
public function fields() {
return [
'id',
'name',
];
}
public function extraFields() {
return ['status'];
}
Thanks
Related
I am using REST API in my project and everything works great. I describe a model using a model
<?php
namespace api\modules\v1\models;
use Carbon\Carbon;
use Yii;
class Comment extends \common\models\Comment
{
public function fields()
{
return [
'id',
'user' => function(Comment $model) {
return User::findOne($model->user_id);
},
'text',
'image' => function(Comment $model) {
return Yii::$app->params['link'].$model->image;
},
'created_at' => function(Comment $model) {
Carbon::setLocale(Yii::$app->language);
return Carbon::createFromTimeStamp(strtotime($model->created_at))->diffForHumans();
},
'children' => function(Comment $model) {
$comments = Comment::find()
->where(['comment_id' => $model->id]);
if (!$comments->exists()) {
return false;
}
return $comments->all();
},
'like',
'news_id',
'comment_id'
];
}
}
The data is returned in the specified format and that's great. But I need to send data to the controller using websockets. For example, when a new comment arrives, send it to all users.
$post = Yii::$app->request->post();
$image = UploadedFile::getInstanceByName('image');
$model = new \api\modules\v1\models\Comment([
'news_id' => $post['feed_id'],
'comment_id' => $post['comment_id'] ?? null,
'user_id' => Yii::$app->user->identity->id,
]);
$model->text = $model->findLinks($post['text']);
if ($image && !$image->error) {
if (!file_exists(Yii::$app->params['comment.pathAbsolute'])) {
if (!FileHelper::createDirectory(Yii::$app->params['comment.pathAbsolute'], 0777)) {
throw new \Exception('Помилка створення папки');
}
}
$serverName = Yii::$app->security->generateRandomString(16).'.'.$image->extension;
if ($image->saveAs(Yii::$app->params['comment.pathAbsolute'].$serverName)) {
$model->image = $serverName;
} else {
throw new \Exception($image->error);
}
}
if (!$model->save()) {
throw new \Exception($model->error());
}
Helper::ws(false, 'updateComment', ['feed_id' => $post['feed_id'], 'comment' => $model]);
And when I pass the $model, the data is passed as it is stored in the database. Is it possible to call a method or something so that the data is passed as I described in the model api?
Is there a way to abstract this Closure to my MerchantUser model in a way similar to using scopes on with()?
So far I have this which works:
$merchant_user->load(['permissions' => function ($query) use ($merchantId) {
if ($merchantId) {
$query->where('merchant_user_permission.merchant_id','=', $merchantId);
}
}]);
But I'd like to do something like this:
$merchant_user->loadPermissions($merchantId);
In my Model:
public function scopeLoadPermissions($query, $merchantId = null)
{
return $query->load(['permissions' => function ($q) use ($merchantId) {
if ($merchantId) {
$q->where('merchant_user_permission.merchant_id','=', $merchantId);
}
}]);
}
Which at the moment just returns an error:
"Method Illuminate\Database\Query\Builder::load does not exist."
For this case you dont need add scope. Instead if you can add this function in your model
public function loadPermissions($merchantId = null)
{
return $this->load(['permissions' => function ($q) use ($merchantId) {
if ($merchantId) {
$q->where('merchant_user_permission.merchant_id','=', $merchantId);
}
}]);
}
and usage
$merchant_user->loadPermissions($merchantId);
I use silverstripe 4.0.3
I made and extension for a Form. I adde the extension via yml to PageController. This is my Extension Class:
class NewsLetterFormExtension extends DataExtension
{
private static $allowed_actions = [
'NewsletterForm'
];
public function NewsletterForm()
{
$form = Form::create(
null,
__Function__,
FieldList::create(
LiteralField::create('Newsletter','<h2>NewsLetter</h2>')
->addExtraClass(''),
LiteralField::create('NLContent','<p>Erfaharen sie regelmäßig was uns beschäftigt</p>')
->addExtraClass(''),
TextField::create('FirstName')
->setAttribute('palceholder', 'Vorname')
->addExtraClass(''),
TextField::create('Surname')
->setAttribute('palceholder', 'Nachname')
->addExtraClass(''),
EmailField::create('Email')
->setAttribute('palceholder', 'E-mail Adresse')
->addExtraClass('')
),
FieldList::create(
FormAction::create('handleNewsletter', 'Senden')
->addExtraClass('btn btn-primary btn-sm')
),
RequiredFields::create('FirstName','Surname', 'Email')
);
return $form;
}
public function handleNewsletter($data, $form)
{
$Newsletter = Newsletter::create();
$form->saveInto($Newsletter);
try {
$Newsletter->write();
} catch (\Exception $e) {
return $e->getMessage();
}
$form->sessionMessage('Danke für die Newsletter Anmeldung', 'good');
return $this->redirectBack();
}
}
As Controller i am passing null. What would be the correct controller to submit it into the extension? Or is it even possible to do so?
I tried to pass it PageController and handle submission there. But I can not get it to work.
An actually i'd would like to submit into it self so that i can add it to multible page types.
You could try extending the Form class. This isn't tested but should work :)
NewsletterForm.php
class NewsletterForm extends Form {
function __construct($controller, $name) {
$form_name = $name;
$fields = FieldList::create(
LiteralField::create('Newsletter','<h2>NewsLetter</h2>')
->addExtraClass(''),
LiteralField::create('NLContent','<p>Erfaharen sie regelmäßig was uns beschäftigt</p>')
->addExtraClass(''),
TextField::create('FirstName')
->setAttribute('palceholder', 'Vorname')
->addExtraClass(''),
TextField::create('Surname')
->setAttribute('palceholder', 'Nachname')
->addExtraClass(''),
EmailField::create('Email')
->setAttribute('palceholder', 'E-mail Adresse')
->addExtraClass('')
);
$actions = FieldList::create(
FormAction::create('handleNewsletter', 'Senden')
->addExtraClass('btn btn-primary btn-sm')
);
$validator = RequiredFields::create('FirstName','Surname', 'Email')
parent::__construct($controller, $form_name, $fields, $actions, $validator);
}
handleNewsletter($data, $form) {
$Newsletter = Newsletter::create();
$form->saveInto($Newsletter);
try {
$Newsletter->write();
} catch (\Exception $e) {
return $e->getMessage();
}
$form->sessionMessage('Danke für die Newsletter Anmeldung', 'good');
return $this->redirectBack();
}
}
NewsletterFormExtension.php
class NewsLetterFormExtension extends DataExtension {
private static $allowed_actions = [
'NewsletterForm'
];
public function NewsletterForm() {
$f = new NewsletterForm($this, 'NewsletterForm');
return $f;
}
}
How should I declare the attributes public function of a class (model) that extends from ActiveRecord if I'm willing to use subdocuments?
Take for example this simple MongoDB structure:
_id: 'a34tfsert89w734y0tas9dgtuwn034t3',
name: 'Bob',
surnames: {
'first': 'Foo',
'second': 'Bar'
},
age: 27,
preferences: {
lang: 'en',
currency: 'EUR'
}
How should my attributes function look like?
public function attributes() {
return [
'_id',
'name',
'surnames',
'surnames.first', <--- like this?
.....
]
}
The MongoDb Extension for Yii 2 does not provide any special way to work with embedded documents (sub-documents). To do that you will need to first deal with custom validations. You could try the following approach: The general pattern is to first build a custom validator, say \common\validators\EmbedDocValidator.php
namespace common\validators;
use yii\validators\Validator;
class EmbedDocValidator extends Validator
{
public $scenario;
public $model;
/**
* Validates a single attribute.
* Child classes must implement this method to provide the actual validation logic.
*
* #param \yii\mongodb\ActiveRecord $object the data object to be validated
* #param string $attribute the name of the attribute to be validated.
*/
public function validateAttribute($object, $attribute)
{
$attr = $object->{$attribute};
if (is_array($attr)) {
$model = new $this->model;
if($this->scenario){
$model->scenario = $this->scenario;
}
$model->attributes = $attr;
if (!$model->validate()) {
foreach ($model->getErrors() as $errorAttr) {
foreach ($errorAttr as $value) {
$this->addError($object, $attribute, $value);
}
}
}
} else {
$this->addError($object, $attribute, 'should be an array');
}
}
}
and model for the embedded document \common\models\Preferences.php
namespace common\models;
use yii\base\Model;
class Preferences extends Model
{
/**
* #var string $lang
*/
public $lang;
/**
* #var string $currency
*/
public $currency;
public function rules()
{
return [
[['lang', 'currency'], 'required'],
];
}
}
And setup the validator in the top-level model
In common\models\User.php:
public function rules()
{
return [
[['preferences', 'name'], 'required'],
['preferences', 'common\validators\EmbedDocValidator', 'scenario' => 'user','model'=>'\common\models\Preferences'],
];
}
The general recommendation is avoiding use of embedded documents moving their attributes at the top level of the document. For example: instead of
{
name: 'Bob',
surnames: {
'first': 'Foo',
'second': 'Bar'
},
age: 27,
preferences: {
lang: 'en',
currency: 'EUR'
}
}
use following structure:
{
name: 'Bob',
surnames_first: 'Foo',
surnames_second: 'Bar'
age: 27,
preferences_lang: 'en',
preferences_currency: 'EUR'
}
which you can then declare as an ActiveRecord class by extending yii\mongodb\ActiveRecord and implement the collectionName and 'attributes' methods:
use yii\mongodb\ActiveRecord;
class User extends ActiveRecord
{
/**
* #return string the name of the index associated with this ActiveRecord class.
*/
public static function collectionName()
{
return 'user';
}
/**
* #return array list of attribute names.
*/
public function attributes()
{
return ['_id', 'name', 'surnames_first', 'surnames_second', 'age', 'preferences_lang', 'preferences_currency'];
}
}
Forgive my English.
I want to redirect not from action side but from a other function.
controller
public function editAction(Request $request, $id)
{
$em = $this->getDoctrine()->getEntityManager();
$article = $em->getRepository('MySampleBundle:Article')->find($id);
// TODO:
// Since other actions have the same processing,
// I would like to do check work in other function.
// And when inaccurate,
// I want to make it move from that function to other page.
$this->is_edit_granted($article);
$form = $this->createForm(new ArticleType(), $article);
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// ...
}
}
return $this->render('MySampleBundle:Article:edit.html.twig', array(
'form' => $form->createView(),
'article' => $article,
));
}
public function is_edit_granted($article)
{
// TODO:
// I check not at the action side but at this place,
// and want to make it move from this function to other page.
if (!$article) {
throw $this->createNotFoundException('No article found.');
} else if ( $article->getAuthor() != $this->getUser()->getId() ) {
return $this->redirect( // doesn't work here
$this->generateUrl('home', array(
"id" => $article->getId()
))
);
}
}
I also tried similar code:
use Symfony\Component\HttpFoundation\RedirectResponse;
class SampleController extends Controller
{
// ...
public function editAction(Request $request, $id)
{
// ...
$this->is_edit_granted($article);
// ...
}
public function is_edit_granted($article)
{
if (!$article) {
throw $this->createNotFoundException('No article found.');
} else if ( $article->getAuthor() != $this->getUser()->getId() ) {
return new RedirectResponse(
$this->generateUrl('home', array(
"id" => $article->getId()
))
);
}
}
}
but it doesn't work.
It is performing in the environment of Symfony 2.1.2.
How can I manage to achieve that?
Or, is there any better method?
Do something like:
public function editAction(Request $request, $id)
{
// ...
$response = $this->is_edit_granted($article);
if ($response) return $response;
// ...
}
public function is_review_granted($article)
{
if (!$article) {
throw $this->createNotFoundException('No article found.');
} else if ( $article->getAuthor() != $this->getUser()->getId() ) {
return new RedirectResponse(
$this->generateUrl('home', array(
"id" => $article->getId()
))
);
}
return null;
}
It is not possible to redirect from the is_review_granted without returning the RedirectResponse form the editAction. So the answer of Carlos Granados is correct.
Another option would be to throw an AccessDeniedException in the is_review_granted method:
public function is_review_granted($article)
{
if (!$article) {
throw $this->createNotFoundException('No article found.');
} else if ( $article->getAuthor() != $this->getUser()->getId() ) {
throw new Symfony\Component\Security\Core\Exception\AccessDeniedException('no acces');
}
}
You could also look to some more in-depth solutions like ACL and SecurityVoters.