I have a collection called work-monitor where-in I have two fields namely
assignor_remarks and assignee_remarks.
so when a comment is submitted by either assignor or assignee, I want to add those comments in the respective comment filed.
I am able to save the comments in the collection, but new comments is overriding the existing one.
my code is like this:
public function actionWorkUpdate($id)
{
\Yii::$app->request->enableCsrfValidation = false;
$work = $this->modelClass::find()->where(['_id'=>$id])->one();
$work->load(Yii::$app->getRequest()->getBodyParams(), '');
$work->assignee_remarks = ["timestamp"=>date('d-m-Y h:i'),"comments"=>$work->assignee_remarks];
$work->update();
return "success";
}
how I can achieve this.
update like in the example below:
"assignee_remarks":{"comment":"test comment","commentTime":2020-04-29 12.41},
{"comment":"test comment2","commentTime":2020-04-29 12.45},
{"comment":"test comment3","commentTime":2020-04-29 12.50}
Try something like that, if I have understood you correctly.
// In Work Model
public $assignee_remarks;
public function rules()
{
return [
//...
['assignee_remarks', 'safe'] // for free load
];
}
// In controller
/**
* In bodyParams you have new comment like assignee_remarks: 'some text'
* #param $id
* #return mixed
*/
public function actionWorkUpdate($id)
{
\Yii::$app->request->enableCsrfValidation = false;
$work = $this->modelClass::find()->where(['_id' => $id])->one();
$currentComments = $work->assignee_remarks ?? [];
$work->load(Yii::$app->getRequest()->getBodyParams(), '');
$currentComments[] = ["commentTime" => date('d-m-Y h:i'), "comment" => $work->assignee_remarks];
$work->assignee_remarks = $currentComments;
$result = $work->update();
if ($result === false) {
// validation error
} else {
return $result > 0 ? 'success' : 'fail';
}
}
Related
I have multiple database tables which each one of them populates a dropdown, The point is each dropdown effects next dropdown.
I mean if I select an item from the first dropdown, the next dropdown values change on the selected item, which means they are related and they populate dynamically on each dropdown item change.
I know this is not efficient and need refactoring so I'd be glad to point me out to the right way of populating those dropdowns.
This is the controller code:
<?php
use App\Http\Controllers\Controller;
use App\Models\County;
use App\Models\OutDoorMedia;
use App\Models\Province;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class FiltersController extends Controller
{
protected static $StatusCode = 0;
protected static $Msg = '';
protected static $Flag = false;
public function index(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'province_id' => 'exists:province,id',
'county_id' => 'exists:county,id',
'media_type' => Rule::in([MEDIA_TYPE_BILLBOARD, MEDIA_TYPE_OUTDOOR_MONITOR, MEDIA_TYPE_INDOOR_MONITOR, MEDIA_TYPE_STAND, MEDIA_TYPE_STRABOARD, MEDIA_TYPE_BRAND_BOARD]),
'media_status' => Rule::in([MEDIA_STATUS_AVAILABLE, MEDIA_STATUS_NEGOTIATING, MEDIA_STATUS_ASSIGNED, MEDIA_STATUS_ARCHIVE]),
'category_id' => 'exists:category_list,id',
]);
if ($validator->fails()) {
abort(400, $validator->errors()->first());
} else {
//############################# Input Filters ####################################
$province_id = $request->has('province_id') ? $request->province_id : null;
$county_id = $request->has('county_id') ? $request->county_id : null;
$media_type = $request->has('media_type') ? $request->media_type : null;
$location = $request->has('location') ? $request->location : null;
$media_status = $request->has('media_status') ? $request->media_status : null;
$category_id = $request->has('category_id') ? $request->category_id : null;
//this flag is for detecting if user is requesting from "advertiser my order" to ignore some concitions
$advertiser_my_orders = ($request->has('my_orders') && $request->my_orders == 'true') ? true : false;
$province_ids = [];
$county_ids = [];
//############################# Media Owner Filters ####################################
//offline section filters
if (!is_null($province_id) && Province::whereId($request->province_id)->exists()) {
//check correction of county id
if (!is_null($county_id) && County::whereId($county_id)->whereProvinceId($province_id)->exists()) {
$media_owner_ODM_Provinces = Province::whereHas('media')->get()->toArray();
$media_owner_ODM_Provinces_Counties = County::whereProvinceId($province_id)
->whereHas('county_media')->get()->toArray();
foreach ($media_owner_ODM_Provinces as $key => $province) {
if ($province['id'] == $province_id) {
$media_owner_ODM_Provinces[$key]['county'] = $media_owner_ODM_Provinces_Counties;
}
}
$media_owner_ODM_locations = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
} else {
$media_owner_ODM_Provinces = Province::whereHas('media')->with(['county' => function ($query) {
$query->whereHas('county_media');
}])->get()->toArray();
$media_owner_ODM_locations = OutDoorMedia::whereProvinceId($province_id)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereProvinceId($province_id)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereProvinceId($province_id)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
}
} else {
$media_owner_ODM_Provinces = Province::whereHas('media')->with(['county' => function ($query) {
$query->whereHas('county_media');
}])->get()->toArray();
foreach ($media_owner_ODM_Provinces as $province) {
$province_ids[] = $province['id'];
foreach ($province['county'] as $county) {
$county_ids[] = $county['id'];
}
}
$media_owner_ODM_locations = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
}
$media_owner_offline = [
'provinces' => $media_owner_ODM_Provinces,
'media_status' => $media_owner_ODM_media_Status,
'location' => $media_owner_ODM_locations,
'media_type' => $media_owner_ODM_media_types,
];
$filters['media_owner']['offline'] = $media_owner_offline;
self::$StatusCode = 200;
self::$Msg = $filters;
self::$Flag = true;
}
} catch (\Exception $e) {
//=========== Get Error Exception Message ============
self::$StatusCode = 400;
self::$Msg = $e->getMessage();
self::$Flag = false;
return $this->CustomeJsonResponse(self::$Flag, self::$StatusCode, self::$Msg);
//=========== Get Error Exception Message ============
} finally {
return $this->CustomeJsonResponse(self::$Flag, self::$StatusCode, self::$Msg);
}
}
}
FYI: I'm using laravel 5.3 framework.
Here are something that you should be aware of them.
first of all, if you validate province_id, so there is no need to double check it in your code. so you should remove
Province::whereId($request->province_id)->exists()
Second one is, Laravel has ->when eloquent method that helps you reduce if else statements for null values, if we have a null value for given parameter, it will not effect the query.
https://laravel.com/docs/5.8/queries#conditional-clauses
Third one, I suggest you to use Laravel Resources in order to transform your fetched data from database in API.
https://laravel.com/docs/5.8/eloquent-resources
This is better version of small portion of your code, I think with suggested tips and this code, you can refactor it:
class TestController extends Controller
{
const DEFAULT_COUNTRY_ID = '10';
public $request;
public function something(Request $request)
{
// Put Validations ...
$this->request = $request;
OutDoorMedia::when('province_id', function ($query) {
return $query->where('province_id', $this->request->province_id);
})
->when('country_id', function ($query) {
// if country_id exists
return $query->where('country_id', $this->request->country_id);
}, function ($query) {
// else of above if (country_id is null ...)
return $query->where('country_id', self::DEFAULT_COUNTRY_ID);
})
->get();
}
}
This is just a sample, You can use this way to refactor your code base.
I am using FOSElasticaBundle and Symfony. I have a method to return some results merged from two types of objects:
public function getMergedResults($query)
{
//See https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/doc/cookbook/multi-type-search.md
$indexManager = $this->get('fos_elastica.index_manager');
$transformer = $this->get('app.elastica_to_model.transformer.snapshot');
/* #var \Elastica\Search $search */
$search = $indexManager->getIndex('app')->createSearch();
$search->addType('snapshot');
$search->addType('traffic_company');
$search->setQuery($query);
$resultSet = $search->search();
$transformer = $this->get('fos_elastica.elastica_to_model_transformer.collection.app');
$resultSet = $transformer->transform($resultSet->getResults());
return $resultSet;
}
... which works well. Now I want to add a filter that was left to me by a previous developer:
protected function addSiteFilter(Query\BoolQuery $boolFilter, SiteInterface $site = null)
{
if ($site) {
$termFilter = new Query\Term(array('site' => array('value' => $site->getId())));
$boolFilter->addMust(array(
$termFilter
));
}
}
... but I'm not sure how to glue these two pieces together. What code can I add to the first function so that it integrates the second one?
I'll provide an example, If it wouldn't be so clear for you, ping me.
/**
* #param string $ownerId
* #return Query
*/
public function getByOwnerId(string $ownerId): Query
{
$terms = new Terms('ownerId', [$ownerId]);
$boolQuery = (new BoolQuery())->addMust($terms);
return (new Query())->setQuery($boolQuery);
}
I have a page called EventPage that I am managing via a Model admin. Also using Catalog manager: https://github.com/littlegiant/silverstripe-catalogmanager
Question is I need to be able to export all the expired events (And all of the fields).
I have an 'EndDate' => 'Date', field on the EventPage.
So I want to only show EventPages in my CSV export where the EndDate is GreaterThanOrEqual to todays date e.g Expired.
The following generates an CSV export button, but currently it is exporting all the fields, where as I want to filter it so we only show the expired events.
How do I go about this?
<?php
class EventAdmin extends CatalogPageAdmin {
public $showImportForm = false;
private static $managed_models = array(
'EventPage',
'EventCategory',
'EventSubmission',
);
private static $url_segment = 'events';
private static $menu_title = 'Events';
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$gridFieldName = 'EventPage';
$gridField = $form->Fields()->fieldByName($gridFieldName);
if ($gridField) {
$gridField->getConfig()->addComponent(new GridFieldExportButton());
}
return $form;
}
}
We can create a custom export button to filter the list of items before exporting them.
First we create a GridFieldExportExpiredEventsButton which extends GridFieldExportButton. This is a complete copy of the current SilverStripe 3.5 generateExportFileData function but with an added filterByCallback on the $items list to filter items that have an EndDate < date('Y-m-d').
class GridFieldExportExpiredEventsButton extends GridFieldExportButton {
public function getHTMLFragments($gridField) {
$button = new GridField_FormAction(
$gridField,
'export',
'Export expired events',
'export',
null
);
$button->setAttribute('data-icon', 'download-csv');
$button->addExtraClass('no-ajax action_export');
$button->setForm($gridField->getForm());
return array(
$this->targetFragment => '<p class="grid-csv-button">' . $button->Field() . '</p>',
);
}
public function generateExportFileData($gridField) {
$separator = $this->csvSeparator;
$csvColumns = $this->getExportColumnsForGridField($gridField);
$fileData = '';
$member = Member::currentUser();
if($this->csvHasHeader) {
$headers = array();
// determine the CSV headers. If a field is callable (e.g. anonymous function) then use the
// source name as the header instead
foreach($csvColumns as $columnSource => $columnHeader) {
$headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader;
}
$fileData .= "\"" . implode("\"{$separator}\"", array_values($headers)) . "\"";
$fileData .= "\n";
}
//Remove GridFieldPaginator as we're going to export the entire list.
$gridField->getConfig()->removeComponentsByType('GridFieldPaginator');
$items = $gridField->getManipulatedList();
$items = $items->filterByCallback(function($item) {
// The following line modifies what items are filtered. Change this to change what items are filtered
return $item->EndDate < date('Y-m-d');
});
// #todo should GridFieldComponents change behaviour based on whether others are available in the config?
foreach($gridField->getConfig()->getComponents() as $component){
if($component instanceof GridFieldFilterHeader || $component instanceof GridFieldSortableHeader) {
$items = $component->getManipulatedData($gridField, $items);
}
}
foreach($items->limit(null) as $item) {
if(!$item->hasMethod('canView') || $item->canView($member)) {
$columnData = array();
foreach($csvColumns as $columnSource => $columnHeader) {
if(!is_string($columnHeader) && is_callable($columnHeader)) {
if($item->hasMethod($columnSource)) {
$relObj = $item->{$columnSource}();
} else {
$relObj = $item->relObject($columnSource);
}
$value = $columnHeader($relObj);
} else {
$value = $gridField->getDataFieldValue($item, $columnSource);
if($value === null) {
$value = $gridField->getDataFieldValue($item, $columnHeader);
}
}
$value = str_replace(array("\r", "\n"), "\n", $value);
$columnData[] = '"' . str_replace('"', '""', $value) . '"';
}
$fileData .= implode($separator, $columnData);
$fileData .= "\n";
}
if($item->hasMethod('destroy')) {
$item->destroy();
}
}
return $fileData;
}
}
The extra line that we have added that filters the export items is:
return $item->EndDate < date('Y-m-d');
Alter this to alter the list of items that are exported. I have set this to only return items which have an EndDate that is in the past. Change this as you need.
We then add this export button to our grid field in our event model admin:
class EventAdmin extends CatalogPageAdmin {
private static $managed_models = array(
'EventPage'
);
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id);
if ($this->modelClass == 'EventPage') {
$gridField = $form->Fields()->fieldByName($this->modelClass);
$gridField->getConfig()->removeComponentsByType('GridFieldExportButton');
$gridField->getConfig()->addComponent(new GridFieldExportExpiredEventsButton('buttons-before-left'));
}
return $form;
}
}
This was originally two answers...
To limit the fields
Have you had a look at the GridFieldExportButton class ?
The constructor
/**
* #param string $targetFragment The HTML fragment to write the button into
* #param array $exportColumns The columns to include in the export
*/
public function __construct($targetFragment = "after", $exportColumns = null) {
$this->targetFragment = $targetFragment;
$this->exportColumns = $exportColumns;
}
So you should be able to pass $exportColumns as an argument.
in your code that would be
if ($gridField) {
$gridField->getConfig()->addComponent(new GridFieldExportButton("after", ["field1", "field2"]));
}
OR - EVEN BETTER SOLUTION
you can define your summary fields on EventPage as such
private static $summary_fields = ["FIeld1", "Field2"];
Then make sure your flush and it should use that as fields.
To filter which items to export in your CSV
So in this case, I think you should create a new class that extends GridFieldExportButton (maybe called EventPageCSVExportButton or something) and override the methods you want. In your case it would probably be generateExportFileData(), just do a check in the loop and exclude data you don't want.
Then use that new class in your EventAdmin.
Have you had a look at the GridFieldExportButton class ?
The constructor
/**
* #param string $targetFragment The HTML fragment to write the button into
* #param array $exportColumns The columns to include in the export
*/
public function __construct($targetFragment = "after", $exportColumns = null) {
$this->targetFragment = $targetFragment;
$this->exportColumns = $exportColumns;
}
So you should be able to pass $exportColumns as an argument.
in your code that would be
if ($gridField) {
$gridField->getConfig()->addComponent(new GridFieldExportButton("after", ["field1", "field2"]));
}
OR - EVEN BETTER SOLUTION
you can define your summary fields on EventPage as such
private static $summary_fields = ["FIeld1", "Field2"];
Then make sure your flush and it should use that as fields.
Actually,
as you see below code,
in model two functions are calling
$this->sendMailtoTutor(),$this->sendMailtoLearner()
i want to check a condition in model that if that condition will true then only these both function will call otherwise not. Is there any way to pass attributes or variable so that i can check condition in aftersave().
my code is as below
in controller
public function actionBooking()
{
$booking_temp = new BookingTemp();
$this->performAjaxValidation($booking_temp);
if (Yii::app()->request->isPostRequest && Yii::app()->request->getPost('BookingTemp'))
{
$booking_temp->attributes = Yii::app()->request->getPost('BookingTemp');
//echo '<pre>';print_r($booking_temp->attributes);exit;
if ($booking_temp->validate())
{
$extra_price = 0;
$post_data = Yii::app()->request->getPost('BookingTemp');
$cam = Cam::model()->findByPk($post_data['temp_cam_id']);
$data = array();
$data = $post_data;
$data['temp_book_user_id'] = Yii::app()->user->id;
$data['temp_book_cam_price'] = $cam->cam_price;
$data['temp_book_duration'] = $cam->cam_duration;
if ($post_data['temp_book_session'] == 2) {
$data['temp_book_cam_price'] = 2 * $cam->cam_price;
$data['temp_book_duration'] = 2 * $cam->cam_duration;
}
if ($post_data['temp_book_is_extra'] == "Y") {
$extra_price = $cam->camExtras->extra_price;
$data['temp_book_extra_price'] = $extra_price;
}
$price_calculation = CamBooking::price_calculation(Yii::app()->user->country_id, $data['temp_book_cam_price'], $extra_price);
$data['temp_book_processing_fees'] = $price_calculation['processing_fees'];
$data['temp_book_service_tax'] = $price_calculation['service_tax'];
$data['temp_book_total_price'] = $price_calculation['total_price'];
$booking_temp->temp_value = serialize($data);
$booking_temp->user_id = Yii::app()->user->id;
$booking_temp->tutor_id = $cam->tutor_id;
$booking_temp_variable = 'check_aftersave';
$booking_temp->save(false, $booking_temp_variable);
//$booking_temp->saveAttributes(array('tutor_id', 'user_id', 'temp_value'));
$created_at = Yii::app()->localtime->fromUTC($booking_temp->created_at);
$created_at_time = strtotime($created_at);
$end_time = $created_at_time + (60 * 3); // 3 min greater from created
$end_time_format = date("Y/m/d H:i:s", $end_time);
echo json_encode(array(
'status' => 'success',
'temp_guid' => $booking_temp->temp_guid,
'end_time_format' => $end_time_format,
), JSON_UNESCAPED_SLASHES);
Yii::app()->end();
} else {
$error = CActiveForm::validate($booking_temp);
if ($error != '[]')
echo $error;
Yii::app()->end();
}
}
}
in model
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->sendMailtoTutor();
$this->sendMailtoLearner();
if ($this->is_message == 'Y' && !empty($this->book_message))
{
Message::insertMessage($this->book_message, $this->book_user_id, $this->cam->tutor_id, $this->cam_id);
}
$user_profile_link = CHtml::link($this->bookUser->fullname, array("/site/user/profile", "slug" => $this->bookUser->slug));
$cam_link = CHtml::link($this->cam->cam_title, array("/site/cam/view", "slug" => $this->cam->slug));
$message = "You have a new booking from {$user_profile_link} for your {$cam_link}";
Notification::insertNotification($this->cam->tutor_id, $message, 'book', $this->book_id);
}
return parent::afterSave();
}
How can I perform this?
Hi See if you want to do it in the afterSave() of BookingTemp Model;. Then you can define a variable in that model like
like
class BookingTemp extends CActiveRecord
{
public $booking_temp_variable;
............
}
now assign this variable before saving your model in actionBooking();
as
$booking_temp->booking_temp_variable = 'check_aftersave';
$booking_temp->save(false);// no need to pass a varible
now in your aftersave function you can easily get it like
protected function afterSave()
{
if ($this->isNewRecord)
{
$this->booking_temp_variable; // this has your value
}
return parent::afterSave();
}
Don't forget to keep it in rules as safe
public function rules(){
// your rules
array('booking_temp_variable','safe');
}
What exacly you want to do?
At first you have to know, that when you are calling save() method and u pass there two args - false and, $booking_temp_variable . The first arg says that you do not run validation, the second one is (should be) array of attributes.
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#save-detail
I have this function:
/**
* #Secure(roles="IS_AUTHENTICATED_FULLY")
* #Route("/chequearFabricanteDistribuidor", name="chequearFabricanteDistribuidor", condition="request.headers.get('X-Requested-With') == 'XMLHttpRequest'")
* #Method("GET")
*/
public function chequearFabricanteAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('AppBundle:FabricanteDistribuidor')->findOneBy(
array( 'nombre' => $request->query->get('fabricante')['nombre'] )
);
$response['valid'] = true;
if ($entities) {
$response['valid'] = false;
}
return new JsonResponse($response);
}
The function needs to be called from two different forms and the only different is the request var that holds the value. In the first form is: $request->query->get('fabricante')['nombre'] while in the second is $request->query->get('distribuidor')['nombre'] so I'm asking if the right way to handle this could be:
if (isset($request->query->get('fabricante')))
{
$nombre = $request->query->get('fabricante')['nombre'];
}
else
{
$nombre = $request->query->get('distribuidor')['nombre'];
}
Is this right? Exists a better one?
As Cerad posted on the responses, you could use:
$request->query->has('distribuidor')