I added new relationship to my Schema in Catalyst project:
__PACKAGE__->has_many(
'messageslog' => "myproject::Schema::Result::MessagesLog",
sub {
my $args = shift;
return {
"$args->{foreign_alias}.from" => "$args->{self_alias}.msisdn",
"$args->{foreign_alias}.service_id" => "3",
"$args->{foreign_alias}.date_time" => {between => ["2013-01-01 00:00:00", "2013- 01-05 23:59:59"]}
};
}
);
When I make request:
$c->model('DB::TableClass')->search({})->hashref_array;
it works perfectly.
But i need to insert search params like this:
__PACKAGE__->has_many(
'messageslog' => "myproject::Schema::Result::MessagesLog",
sub {
my $args = shift;
return {
"$args->{foreign_alias}.from" => "$args->{self_alias}.msisdn",
"$args->{foreign_alias}.service_id" => "$args->{params}->{id}",
"$args->{foreign_alias}.date_time" => {between => $args->{params}->{dates}}
};
}
);
$c->model('DB::TableClass')->search({}, {
join => 'messageslog',
-params => {id => 3, dates => ["2013-01-01 00:00:00", "2013-01-05 23:59:59"]}
})->hashref_array;
How can I do this?
By adding a method to the ResultSet instead of a relationship, see Cookbook/Predefined-searches.
Related
i am new here, i have a problem with an api in laravel.
I want to show all the records from two tables.
Table a : Flights;
Table b: Details_flights;
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::all();
$details = Flights_detail::all();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $flights
]);
}
}
}
I tried with array_merge(table A, table B) but it didn't work.
Someone can help me?
You can convert them to array, then merge them in a new array called for example data. Below code may help you:
$data=[
'flights' => $flights->toArray(),
'details' => $details->toArray(),
];
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $data
]);
You can try this https://stackoverflow.com/a/30524136/1529662
And your $flights and $details variables are collection. If you want to convert, you should use toArray() method.
There can be two scenarios:
Flight record hasOne/hasMany FlightDetail records (related)
flight_details table will have a foreign key flight_id
Flight and FlightDetail are not related
flight_details table doesn't have foreign key flight_id
Scenario 1
//Models
class Flight extends Model
{
public function details()
{
return $this->hasMany(FlightDetail::class, 'flight_id', 'id');
}
}
class FlightDetail extends Model
{
public function flight()
{
return $this->belongsTo(Flight::class, 'flight_id', 'id');
}
}
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::with('details`)->get();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $flights
]);
}
}
}
Scenario 2
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::all();
$details = Flights_detail::all();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => [
'flights' => $flights->count(),
'flightDetails' => $details->count()
],
'results' => [
'flights' => $flights,
'flightDetails' => $details
]
]);
}
}
}
I have used Left Join Query in file called "directoryDataTable.php. Now the problem is that Yajra DataTable search bar is not working. Its neither giving any error nor the search result.
My DataTable query function is al follows.
public function query()
{
$id = \Illuminate\Support\Facades\Auth::user()->id;
$directories = DB::table('directories')
->leftjoin('claimed', 'directories.id', '=','claimed.dir_id')
->select('directories.*')
->where('directories.user_id',$id)
->where('paymentStatus','1')
->whereNull('directories.deleted_at')
->orWhere('claimed.claimed_by',$id);
return $this->applyScopes($directories);
}
Please Help
Changed the query to,
public function query()
{
$id = \Illuminate\Support\Facades\Auth::user()->id;
$directories = DB::table('directories')
->leftjoin('claimed', 'directories.id', '=','claimed.dir_id')
->select('directories.*')
->where(function ($query) {
$query->where('directories.user_id',\Illuminate\Support\Facades\Auth::user()->id)
->orWhere('claimed.claimed_by',\Illuminate\Support\Facades\Auth::user()->id);
})
->where('paymentStatus','1')
->whereNull('directories.deleted_at')
;
return $this->applyScopes($directories);
}
and in get columns function, replace ['name' => 'YourTableName.ColumnName','data' => 'YourColumnName'] Like this.
private function getColumns()
{
return [
'dir_name' => ['name' => 'directories.dir_name', 'data' => 'dir_name'],
'phone_number' => ['name' => 'directories.phone_number', 'data' => 'phone_number'],
'address' => ['name' => 'directories.address', 'data' => 'address'],
'features' => ['name' => 'directories.features', 'data' => 'features', ],
'Status' => ['name' => 'directories.Status', 'data' => 'Status','searchable'=>false ],
'Subscription' => ['name' => 'directories.Subscription', 'data' => 'Subscription','searchable'=>false ]
];
}
I am using wbraganca dynamic form widget. It works fine for the Create action.
Let me thanks for those guys making great tutorial video on youtube!!!
I am working on the Update action now. I work it on a purchase order function.
the controller of update action :
public function actionUpdate($id)
{
$model = $this->findModel($id);
$modelsItem = $model->purchaseitems;
if ($model->load(Yii::$app->request->post()) ) {
$oldIDs = ArrayHelper::map($modelsItem, 'purchaseitem_id', 'purchaseitem_id');
$modelsItem = Model::createMultiple(Purchaseitem::classname(), $modelsItem);
Model::loadMultiple($modelsItem, Yii::$app->request->post());
$deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsItem, 'purchaseitem_id', 'purchaseitem_id')));
$valid = $model->validate();
$valid = Model::validateMultiple($modelsItem) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
if (! empty($deletedIDs)) {
Purchaseitem::deleteAll(['purchaseitem_id' => $deletedIDs]);
}
foreach ($modelsItem as $modelItem) {
$modelItem->purchaseitem_purchaseorder_id = $model->purchaseorder_id;
$modelItem->purchaseitem_description = Inventory::findOne($modelItem->purchaseitem_inventory_id)->inventory_partno;
if (! ($flag = $modelItem->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $model->purchaseorder_id]);
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
//return $this->redirect(['view', 'id' => $model->purchaseorder_id]);
} else {
return $this->render('update', [
'model' => $model,
'modelsItem' => (empty($modelsItem)) ? [new Purchaseitem] : $modelsItem
]);
}
}
But I think the problem may happen on the view file, as the Select2 field can show the value, which is the 'id' of the product rather than the product code.
view:
<div class="panel panel-default">
<div class="panel-body">
<?php DynamicFormWidget::begin([
'widgetContainer' => 'dynamicform_wrapper',
'widgetBody' => '.container-items',
'widgetItem' => '.item',
'limit' => 50,
'min' => 1,
'insertButton' => '.add-item',
'deleteButton' => '.remove-item',
'model' => $modelsItem[0],
'formId' => 'dynamic-form',
'formFields' => [
'purchaseitem_inventory_id',
'purchaseitem_qty',
'purchaseitem_cost_usd',
'purchaseitem_deliverydate',
],
]); ?>
<?php foreach ($modelsItem as $i => $modelItem): ?>
<div class="item">
<?php
// necessary for update action.
if (! $modelItem->isNewRecord) {
echo Html::activeHiddenInput($modelItem, "[{$i}]purchaseitem_id");
}
?>
<div class="row">
<?= $form->field($modelItem, "[{$i}]purchaseitem_inventory_id")->widget(
Select2::classname(), [
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 2,
'language' => [
'errorLoading' => new JsExpression("function () { return 'Error on finding results...'; }"),
],
'ajax' => [
'url' => Url::toRoute('inventory/inventorylist'),
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
'templateSelection' => new JsExpression('function (purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
],
])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_qty")->textInput(['maxlength' => true])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_cost_usd")->textInput(['maxlength' => true])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_deliverydate")->widget(
DatePicker::className(), [
'options' => [
['placeholder' => 'Please enter delivery date'],
],
'removeButton' => false,
'pluginOptions' => [
'autoclose'=>true,
'format' => 'yyyy-mm-dd',
'todayHighlight' => true,
]
]
)->label(false) ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
I have a thought that the problem maybe related to that few lines of JsExpression function.
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
'templateSelection' => new JsExpression('function (purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
For the Select2 query URL method is here:
public function actionInventorylist($q = null, $id = null) {
Yii::$app->response->format = yii\web\Response::FORMAT_JSON;
$out = ['results' => ['id' => '', 'text' => '']];
if (!is_null($q)) {
$query = new Query;
$query->select('inventory_id AS id, inventory_partno AS text')
->from('inventory')
->where(['like', 'inventory_partno', $q])
->limit(10);
$command = $query->createCommand();
$data = $command->queryAll();
$out['results'] = array_values($data);
}
elseif ($id > 0) {
$out['results'] = ['id' => $id, 'text' => Inventory::find($id)->inventory_partno];
}
return $out;
}
I can load the record, when I click in the update view. Most of the data are feed in right place of the form, except the 'partno' field. I use Select2 to let user select partno by text and store the 'id' in table. It works on the Create view.
but in the update view, it only show the 'id' instead of the 'partno'.
if I make input to the field, I can select 'other' partno only, let me explain here:
if there are 2 code, "ABC" with 'id' is 1, "XYZ" with 'id' 2.
the record original is "ABC", the field show "1".
If I input "XYZ", it will show "XYZ" as normal effect of widget. But, if I change back to "ABC", it will show "1" instead of "ABC".
And the form also cannot submit for update. the button click with no effect.
I am new to Yii2 framework, and quite stuck on this issue, does anyone knows how can I solve this?
THANKS!!!!
I just solve the problem, a few issue happened actually.
To solve the Select2 widget cannot display the partno instead of the ID, I find the partno by the ID and feed it with initValueText in Select2. For my case:
$partno = empty($modelItem->purchaseitem_inventory_id) ? '':Inventory::findOne($modelItem->purchaseitem_inventory_id)->inventory_partno;
$form->field($modelItem, "[{$i}]purchaseitem_inventory_id")->widget(Select2::classname(), ['initValueText' => $partno, ...
About the update POST fail issue, I got error of Getting unknown property: backend\models\Purchaseitem::id, and I found it happened on the Dynamic-form widgets Model.php line 25. That lines is $keys = array_keys(ArrayHelper::map($multipleModels, 'id', 'id'));
If I change the 'id' to my ID field name, i.e 'purchaseitem_id', it will work, but this model will only work for this Id field name afterward. So I get the model's primaryKey and make it work for my other model.
add this line $primaryKey = $model->tableSchema->primaryKey; and modify above line $keys = array_keys(ArrayHelper::map($multipleModels, $primaryKey, $primaryKey));
I have this problem: I need to get data from database and filter them. but then I need to use custom php function to filter those filtered results using data from it.
Clasic search function in ActiveDataProvider
public function search($params) {
$query = Passenger::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
// I guess my function would go like here
Passenger::filterResultsEvenMore($dataProvider);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'passenger_id' => $this->passenger_id,
// ...
'version' => $this->version,
'status' => $this->status,
]);
return $dataProvider;
}
So my question is how to work with results of dataProvider because if I vardump the variable it looks like this and no actual data there.
yii\data\ActiveDataProvider Object
(
[query] => common\models\PassengerQuery Object
(
[sql] =>
[on] =>
[joinWith] =>
[select] =>
[selectOption] =>
[distinct] =>
[from] =>
[groupBy] =>
[join] =>
[having] =>
[union] =>
[params] => Array()
[_events:yii\base\Component:private] => Array()
[_behaviors:yii\base\Component:private] => Array()
[where] => Array
(
[status] => 1
)
[limit] =>
[offset] =>
[orderBy] =>
[indexBy] =>
[emulateExecution] =>
[modelClass] => common\models\Passenger
[with] =>
[asArray] =>
[multiple] =>
[primaryModel] =>
[link] =>
[via] =>
[inverseOf] =>
)
[key] =>
[db] =>
[id] =>
[_sort:yii\data\BaseDataProvider:private] =>
[_pagination:yii\data\BaseDataProvider:private] =>
[_keys:yii\data\BaseDataProvider:private] =>
[_models:yii\data\BaseDataProvider:private] =>
[_totalCount:yii\data\BaseDataProvider:private] =>
[_events:yii\base\Component:private] => Array()
[_behaviors:yii\base\Component:private] =>
)
UPDATE
I need to use function like this for each record:
if (myFunction(table_column_1, table_column_2)) {
result_is_ok_return_it
} else {
do_not_return_this_record
}
Why do you don't add your additional filters to query object used in DataProvider?
You can parse your conditions to $query->andFilterWhere(). If you need custom function for it just modify $dataProvider->query object inside function. After execute query in data provider you can only filter results by manually filter array of models stored in $dataProvider->models
To get result use models property or getModels()
For example,
$dataProvider->models;
OR
$dataProvider->getModels();
I think I came across a solution, (looks like it is working)
http://www.yiiframework.com/doc-2.0/yii-data-basedataprovider.html#setModels()-detail
After I do all my usual search stuff as described in question at beginning, I would do something like this using setModels() function
class PassengerSearch extends Passenger
public $status; // virtual attribute not present in database table
public function rules()
{
return [
// ... some other rules
[['status'], 'safe'],
];
}
// ...
$filtered_models = [];
$filter_models = false; // if you only want to filter if there is some value
foreach ($dataProvider->models as $model) {
// if ($model->status == 1) // example
if (!empty($this->status) && $model->status == $this->status) { // better approach, using virtual attribute $status
$filter_models = true;
$filtered_models[] = $model;
}
}
if ($filter_models)
$dataProvider->setModels($filtered_models);
return $dataProvider;
}
Bug or Feature? If I change request data with beforeMarshal and there is a validation error, the request data will not be given back modified.
This question may be related to How to use Trim() before validation NotEmpty?.
Modifying Request Data Before Building Entities
If you need to modify request data before it is converted into entities, you can use the Model.beforeMarshal event. This event lets you manipulate the request data just before entities are created. Source: CakePHP 3 Documentation
According to the book I would expect the request data is always changed, no matter if there is a validation error or not.
Example or test case:
// /src/Model/Table/UsersTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
// Required for beforeMarshal event:
use Cake\Event\Event;
use ArrayObject;
// Required for Validation:
use Cake\Validation\Validator;
class UsersTable extends Table {
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options) {
$data['firstname'] = trim($data['firstname']);
}
public function validationDefault(Validator $validator) {
$validator
->add('firstname', [
'minLength' => [ 'rule' => ['minLength', 2], 'message' => 'Too short.' ],
])
;
return $validator;
}
}
If I enter " d" (Space-d) the validation error is shown, but the space itself is not removed in the form. I would expact the form showing only "d" because the space is removed from the request data with the beforeMarshal event. So... bug or feature?
My solution would be to use the trim()-function in the controller instead of the beforeMarshal event:
// /src/Controller/UsersController.php
// ...
public function add() {
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
// Use trim() here instead of beforeMarshal?
$this->request->data['firstname'] = trim($this->request->data['firstname']);
$user = $this->Users->patchEntity($user, $this->request->data );
if ( $this->Users->save($user) ) {
$this->Flash->succeed('Saved');
return $this->redirect(['controller' => 'Users', 'action' => 'index']);
} else {
$this->Flash->error('Error');
}
}
$this->set('user', $user);
}
This way the space will be removed even if there is a validation error. Or did I miss another function similar to beforeMarshal which is really modifying the request data?
The main purpose of beforeMarshal is to assist the users to pass the validation process when simple mistakes can be automatically resolved, or when data needs to be restructured so it can be put into the right columns.
The beforMarshal event is triggered just at the start of the validation process, one of the reasons is that beforeMarshal is allowed to change the validation rules and the saving options, such as the field whitelist. Validation is triggered just after this event is finished.
As documentation explains, if a field does not pass validation it will automatically removed from the data array and not be copied into the entity. This is to prevent having inconsistent data in the entity object.
More over, the data in beforeMarshal is a copy of the request. This is because it is important to preserve the original user input, as it may be used elsewhere.
If you need to trim your columns and display the result of the trimming to your user, I recommend doing it in the controller:
$this->request->data = array_map(function ($d) {
return is_string($d) ? trim($d) : $d;
}, $this->request->data);
Not work. This is my beforeMarshal :
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
$schema = $this->schema();
foreach($schema->columns() as $idx => $field ) {
$sc = $schema->getColumn($field);
if (isset($data[$field]) && $data[$field] != null) {
if ($sc['type'] == 'date') {
$date = DateTime::createFromFormat('d/m/Y',$data[$field]);
if ($date)
$data[$field] = $date->format('Y-m-d');
}
if ($sc['type'] == 'datetime') {
debug($data[$field]);
$date = DateTime::createFromFormat('d/m/Y',$data[$field]);
if ($date)
$data[$field] = $date->format('Y-m-d H:i:s');
}
}
}
debug($data);
}
The date commission_approved_date is correctly modified in beforeMarshal:
/src/Model/Table/AccountsTable.php (line 265)
object(ArrayObject) {
_referer => 'http://localhost/gessin/Accounts/edit/ODc?filter=eyJBY2NvdW50cy51c2VyX2lkIjoiMTA4NSIsIjAiOiJNT05USChBY2NvdW50cy5jb21taXNzaW9uX2RhdGUpID4gMCIsIllFQVIoQWNjb3VudHMuY29tbWlzc2lvbl9kYXRlKSI6IjIwMjAifQ=='
description => 'Provvigione su attivazione prodotto vod002'
notes => 'asd'
totalpaid => '0'
commission_approved_date => '2020-02-23 18:34:22'
}
But the same date is not, after patchEntity:
/src/Controller/AccountsController.php (line 203)
object(App\Model\Entity\Account) {
'id' => (int) 87,
'identifier' => null,
'company_id' => null,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-29 14:01:50.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-29 18:30:24.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'notes' => 'asd',
'total' => null,
'totaltax' => null,
'invoice_id' => null,
'description' => 'Provvigione su attivazione prodotto vod002',
'in_out' => null,
'is_refund' => null,
'client_id' => null,
'contract_id' => (int) 32,
'totalpaid' => (float) 0,
'user_id' => (int) 1085,
'commission_date' => object(Cake\I18n\FrozenTime) {
'time' => '2020-02-04 00:00:00.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'commission_approved_date' => object(Cake\I18n\FrozenTime) {
'time' => '2028-08-12 00:00:00.000000+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},