I'm trying to save multiple records via
AppSettings::create(
[
'name' => 'mail_host',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_port',
'type' => $emailsettingstype->id,
'value' => '',
],
[
'name' => 'mail_username',
'type' => $emailsettingstype->id,
'value' => '',
],
);
But from the above, only the first array is getting created. Where am i going wrong? Any help is appreciated.
I think this should do
AppSettings::createMany([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
Make sure you're passing an array of arrays, not a params of array.
UPDATE, you can use Model::insert() although according to what I've read, that method doesn't create/update the timestamps.
You can just use Eloquent::insert() link as below:
AppSettings::insert([
[
'name'=>'mail_host',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_port',
'type'=>$emailsettingstype->id,
'value'=>'',
],
[
'name'=>'mail_username',
'type'=>$emailsettingstype->id,
'value'=>'',
],
]);
The problem with above is that it won't update timestamps, find examples here
The Create many Method createMany is available on relationship check reference to this link and this documentation from laravel
so far my example look like this.
I have two models Pricing and AvailableService Model
Pricing Model
namespace App;
use Illuminate\Database\Eloquent\Model;
class Pricing extends Model
{
protected $fillable = ["name", "price"];
public function available(){
return $this->hasMany(AvailableService::class, "pricing_id", "id");
}
}
And the AvailableServiceMode look like this
namespace App;
use Illuminate\Database\Eloquent\Model;
class AvailableService extends Model
{
protected $fillable = ["pricing_id", "service_id"];
public function service(){
return $this->belongsTo(Service::class, "service_id", "id");
}
}
So createMany operation look like this
$insertMany = Pricing::create(['name'=>request('name')]);
$insertMany->available()->createMany([
['service_id'=>1],
['service_id'=>2],
['service_id'=>3],
['service_id'=>4],
['service_id'=>5],
]);
And it works for, you can give it a try too. THANKS
If you want to store multiple record in seeder use this method instead of insert because in my case I want to slug automatically created using spatie/laravel-sluggable pkg. If you used the insert or DB technique then you have to give the value for slug field also.
CategorySeeder
<?php
namespace Database\Seeders;
use App\Servcategory;
use Illuminate\Database\Seeder;
class CategorySeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$categories = [
[
'name' => 'Automotive',
// 'slug' => 'automotive',
],
[
'name' => 'Business Services',
// 'slug' => 'business-services',
],
[
'name' => 'Computer, Telecom & IT Services',
// 'slug' => 'computer-telecom-&-it-services',
],
[
'name' => 'Education & Training',
// 'slug' => 'education-&-training',
],
[
'name' => 'Finance',
// 'slug' => 'finance',
],
[
'name' => 'Hospitals, Clinic, Medical',
// 'slug' => 'hospitals-clinic-medical',
],
[
'name' => 'Real Estate, Construction, Property',
// 'slug' => 'real-estate-construction-property',
],
[
'name' => 'Travel,Toursim & Hotels',
// 'slug' => 'travel-toursim-&-hotels',
],
];
// Servcategory::insert($categories);
collect($categories)->each(function ($category) { Servcategory::create($category); });
}
}
In case some one searching for eloquent model, I used the following method:
foreach($arCategories as $v)
{
if($v>0){
$obj = new Self(); // this is to have new instance of own
$obj->page_id = $page_id;
$obj->category_id = $v;
$obj->save();
}
}
$obj = new Self(); is a must otherwise it only saves single record when $this is used.
in seeder create an array and do foreach with Model::create(). All your records will be with timestamps
protected $array = [
[...],
[...],
[...]
];
public function run()
{
foreach ($this->array as $value) {
Model::create($value);
}
}
Related
I'm looking to use elastic search on a project with model relation.
For now elastic search is working, I've followed this doc who explain how to start with this package :
elasticsearch/elasticsearch
babenkoivan/elastic-migrations
babenkoivan/elastic-adapter
babenkoivan/elastic-scout-driver
The problem is I need to able to search by relation.
this is my composant elastic migration :
Index::create('composant', function(Mapping $mapping, Settings $settings){
$mapping->text('reference');
$mapping->keyword('designation');
$mapping->join('categorie');
$settings->analysis([
'analyzer' => [
'reference' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
],
'designation' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
]
]
]);
});
Here my categorie elastic migration :
Index::create('categorie', function(Mapping $mapping, Settings $settings){
$mapping->keyword('nom');
$settings->analysis([
'analyzer' => [
'nom' => [
'type' => 'custom',
'tokenizer' => 'whitespace'
]
]
]);
});
My composant Model :
public function categorie()
{
return $this->belongsTo('App\Model\Categorie');
}
public function toSearchableArray()
{
return [
'reference' => $this->reference,
'designation' => $this->designation,
'categorie' => $this->categorie(),
];
}
and my categorie Model :
public function toSearchableArray()
{
return [
'nom' => $this->nom,
];
}
So if you look at the composant relation, you can see that the join mapping return the categorie relation. I dont now if I do it right but what I know is that elasticsearch didn't have any relation in the object I'm looking for.
And I didn't find any doc of how to use the join mapping method of the package.
OK, I've found the solution, the problem was in the migration you must use object in order to index the belongsToMany relationship like that
Index::create('stages', function (Mapping $mapping, Settings $settings) {
$mapping->text('intitule_stage');
$mapping->text('objectifs');
$mapping->text('contenu');
$mapping->object('mots_cles');
});
and in your model :
public function toSearchableArray()
{
return [
'intitule_stage' => $this->intitule_stage,
'objectifs' => $this->objectifs,
'contenu' => $this->contenu,
'n_stage' => $this->n_stage,
'mots_cles' => $this->motsCles()->get(),
];
}
And the result is as expected now
If you want to get "nom" of categorie, write this in composant Model instead
'categorie' => $this->categorie->nom ?? null,
$this->categorie() return the relationship, not the object.
Same problem with a belontoMany relation, and I've made the same things in order to get the relation as a nested object, but when I try to populate my index the field "mots_cles" stay empty, I don't understand why.
Here is the migration :
Index::create('stages', function (Mapping $mapping, Settings $settings) {
$mapping->text('intitule_stage');
$mapping->text('objectifs');
$mapping->text('contenu');
$mapping->nested('motsCles', [
'properties' => [
'mot_cle' => [
'type' => 'keyword',
],
],
]);
});
The model :
public function toSearchableArray()
{
return [
'intitule_stage' => $this->intitule_stage,
'objectifs' => $this->objectifs,
'contenu' => $this->contenu,
'n_stage' => $this->n_stage,
'mots_cles' => $this->motsCles(),
];
}
public function motsCles()
{
return $this->belongsToMany(MotsCle::class);
}
I am creating a custom Identity interface without created_at property. I got an error :
"name": "Unknown Property",
"message": "Setting unknown property: api\\common\\models\\User::created_at",
I tried to comment the TimestampBehavior, but I got the following error:
"name": "PHP Warning",
"message": "Invalid argument supplied for foreach()",
I want to know where is the problem.
Model class:
class User extends ActiveRecord implements IdentityInterface
{
public static function tableName()
{
return '{{%user}}';
}
public function behaviors()
{
// return [
// TimestampBehavior::className(),
// ];
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['purpose'], 'required'],
[['status'], 'integer'],
];
}
}
for the rest controller the action is
public function actionLogin(){
.
.
.
$api_user = new User();
$api_user->purpose="app";
$api_user->status=User::STATUS_ACTIVE;
if($api_user->save()){
$success = true;
}
}
This will automatically resolve the issue. BlameableBehavior and TimestampBehavior
// Include these on the start
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;
use Carbon\Carbon;
// Paste this function inside the class.
/**
* #return array
*/
public function behaviors()
{
return [
'blameable' => [
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'created_by',
'updatedByAttribute' => 'updated_by',
],
'timestamp' => [
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => Carbon::now(),
],
];
}
NOTE: If you are not using updated_at or updated_by then remove it
form the above code
change your Behavior in your model to:
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => new Expression('NOW()'),
],
];
}
if you haven't updated_at also delete it from attributes.
You were getting following warning because you've completely removed the return in the behaviors() method.
"name": "PHP Warning",
"message": "Invalid argument supplied for foreach()",
The behaviors method must return an array. If you don't want to use any behavior your behaviors() method should return empty array like this:
public function behaviors()
{
return [];
}
This is also default implementation of behaviors() method in yii\base\Component so if you don't need to use any behavior you can simply remove the behaviors() method from your model.
Attaching TimestampBehavior to your model when you are not using it means that you add unnecessary overhead.
Example: Rename and prevent time recording or remove properties. Also change the value
Rename or delete properties or change value.
public function behaviors()
{
return [
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
// 'createdAtAttribute' => 'c_time', //Change the name of the field
'updatedAtAttribute' => false, //false if you do not want to record the creation time.
// 'value' => new Expression('NOW()'), // Change the value
],
];
}
Or
'class' => \yii\behaviors\TimestampBehavior::className(),
'attributes' => [
\yii\db\ActiveRecord::EVENT_BEFORE_INSERT => ['created_at'],
// \yii\db\ActiveRecord::EVENT_BEFORE_UPDATE => [],
],
$createdAtAttribute: The attribute that will receive timestamp value Set this property to false if you do not want to record the creation time.
$attributes: List of attributes that are to be automatically filled with the value specified via $value. The array keys are the ActiveRecord events upon which the attributes are to be updated, and the array values are the corresponding attribute(s) to be updated. You can use a string to represent a single attribute, or an array to represent a list of attributes. For example,
[
ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
]
I'm a newbie in Laravel, but I'm using laravel's Maatwebsite\Excel Library v3 to export excel. But I'm having some problems exporting my array data.
here is my code
<?php
namespace App\Exports;
use App\Team;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithHeadings;
class RegisteredMemberExport implements FromCollection, WithHeadings
{
use Exportable;
public function collection()
{
$data = Team::where('reg', 1)->get();
return collect([
[
'name' => $data->name,
'email' => $data->email
]
]);
}
public function headings(): array
{
return [
'name',
'email'
];
}
}
the collect should be
return collect
([
[
'name' => 'Povilas',
'email' => 'povilas#laraveldaily.com'
],
[
'name' => 'Taylor',
'email' => 'taylor#laravel.com'
]
]);
I can't use a loop inside the collect method return.
Can I please have some help?
You can directly filter the values you need from your Eloquent model using a list of attributes as a parameter for the get method.
$data = Team::where('reg', 1)->get(['name', 'email']);
return collect($data->toArray());
You can use each or map or any chain method you want Laravel Collections like this:
return collect([
['name' => 'Povilas','email'=>'povilas#laraveldaily.com'],
['name' => 'Taylor','email' => 'taylor#laravel.com']
])->each(function($value){
return $value; // Do what you want here
});
You can use the Maatwebsite\Excel\Concerns\FromArray concern which works the same as FromCollection.
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
class RegisteredMemberExport implements FromArray, WithHeadings
{
public function array(): array
{
return [
[
'name' => 'Povilas',
'email' => 'povilas#laraveldaily.com',
],
[
'name' => 'Taylor',
'email' => 'taylor#laravel.com',
],
];
}
public function headings(): array
{
return [
'name',
'email',
];
}
}
Source
I want to realize a form, which is quite simple. The only thing that makes things complicated is that I 'm using two collections in my form. Displaying two collections in the view works like a charme. The problem is the validation and the associated hydration of the bound entity of the form. If all is validated and no errors occur the form instance tries to hydrate the bound entity and ends up with an exception:
Zend\Hydrator\ArraySerializable::hydrate expects the provided object to implement exchangeArray() or populate()
But first the example code ...
The form classes
namespace Application\Form;
use Zend\Form\Element\Collection;
use Zend\Form\Element\Text;
use Zend\Form\Form;
class MyForm extends Form
{
public function __construct($name = '', $options = [])
{
parent::__construct($name, $options);
$this->setAttribute('method', 'post');
$this->setAttribute('id', 'my-form');
}
public function init()
{
$this->add([
'name' => 'my-text-field',
'type' => Text::class,
'attributes' => [
...
],
'options' => [
...
],
]);
// The first collection
$this->add([
'name' => 'first-collection',
'type' => Collection::class,
'options' => [
'count' => 2,
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => FieldsetOne::class,
],
],
]);
// the second collection
$this->add([
'name' => 'second-collection',
'type' => Collection::class,
'options' => [
'count' => 2,
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => FieldsetTwo::class,
],
],
]);
}
}
The metioned Fieldset classes which are bound to the collections look pretty much the same.
namespace Application\Form;
use Zend\Form\Element\Number;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class FieldsetOne extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'my-number',
'type' => Number::class,
'options' => [
...
],
'attributes' => [
...
],
]);
}
public function getInputFilterSpecification()
{
return [
'my-number' => [
'required' => true,
'filters' => [
[
'name' => StripTags::class,
],
[
'name' => ToInt::class,
],
],
'validators' => [
[
'name' => NotEmpty::class,
],
[
'name' => IsInt::class,
'options' => [
'locale' => 'de_DE',
],
],
],
],
];
}
}
Summed up the form got two collections of number elements. All data which is provided over the form should end up in the following entity.
The input filter class
The form gets filtered and validated by the following input filter. The input filter will be bound to the form via a factory. The factory will be shown later.
class MyFormInputFilter extends InputFilter
{
public function init()
{
$this->add([
'name' => 'my-text-field',
'required' => true,
'filters' => [
[
'name' => StripTags::class,
],
[
'name' => StringTrim::class,
],
],
]);
}
}
The input filter contains only settings for the my-text-field element. The collections will be validated with the implemented InputFilterProviderInterface in the fieldsets set as target elements. The input filter class is created over a factory and notated in the input_filters section in the module.config.php.
The form entity
The entity will be bound as an object to the form in a factory it looks like the following example.
namespace Application\Entity;
class MyFormEntity
{
protected $myTextField;
protected $firstCollection;
protected $secondCollection;
public function getMyTextField()
{
return $this->myTextField;
}
public function setMyTextField($myTextField)
{
$this->myTextField = $myTextField;
return $this;
}
public function getFirstCollection()
{
return $this->firstCollection;
}
public function setFirstCollection(array $firstCollection)
{
$this->firstCollection = $firstCollection;
return $this;
}
public function getSecondCollection()
{
return $this->secondCollection;
}
public function setSecondCollection(array $secondCollection)
{
$this->secondCollection = $secondCollection;
return $this;
}
}
This entity will be bound as object to the form. The form will be hydrated be zend 's own ClassMethods hydrator class. For the collections two hydrator strategies are added to the hydrator. The hydrator strategies for the collections look like this.
namespace Application\Hydrator\Strategy;
class FirstCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
$aEntities = [];
if (is_array($value)) {
foreach ($value as $key => $data) {
$aEntities[] = (new ClassMethods(false))->hydrate($data, new CollectionOneEntity());
}
}
return $aEntities;
}
}
This strategy will hydrate the data from collection one to the corresponding entity.
All wrapped up in a factory
This is the factory which creates the form instance.
class MyFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();
$filter = $parentLocator->get('InputFilterManager')->get(MyFormInputFilter::class);
$hydrator = (new ClassMethods())
->addStrategy('first-collection', new FirstCollectionStrategy())
->addStrategy('second-collection', new SecondCollectionStrategy());
$object = new MyFormEntity();
$form = (new MyForm())
->setInputFilter($filter)
->setHydrator($hydrator)
->setObject($object);
return $form;
}
}
This factory is mentionend in the form_elements section in the module.config.php file.
The problem
Everything works fine. The input element and also the collections are rendered in the view. If the form is submitted and the $form->isValid() method gets called in the controller all ends up in a BadMethodCallException.
Zend\Hydrator\ArraySerializable::hydrate expects the provided object to implement exchangeArray() or populate()
I have not bound the collection entities to the form in the controller because the hydrator strategies are added to the form hydrator that should hydrate the form entity. This makes sense for me, because zend form can only bind one object. If i call the bind method twice in the controller, the first bound object will be overwritten.
Is it possible to add more than one object with the bind method of the form so two collections can be handled? What could alternatives look like? What I 'm doing wrong?
i am need to sort some fields (asc,desc) in GridView, but same fields are calculated. Look at code below:
SearchModel:
class ObjectSearch extends Object {
use SearchModelTrait;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff'
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Methods in Object model:
public function getLastReportResult()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = new ReportStatistic($lastReport);
$message = $statistic->getPercent();
}
return $message;
}
/**
* #return int
*/
public function getReportPercentDiff()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = $lastReport->getReportDiff();
if (!empty($statistic['diff'])) {
$message = $statistic['diff']['right_answers_percent_diff'];
} elseif (!empty($statistic['message'])) {
$message = $statistic['message'];
}
}
return $message;
}
So, by this methods, i am calculating a values of two fields, which are need's sorting. This way doesn't working, i have a Database Exception, because object table hasn't this fields. exception
How to do sorting of this fields ?
Update: I am the author of this answer and this answer is not accurate. Preferred way is to use database view
Add two public properties to ObjectSearch.php and mark it as safe
class ObjectSearch extends Object {
use SearchModelTrait;
public $lastReportResult, $reportPercentDiff;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
[['lastReportResult', 'reportPercentDiff'], 'safe']
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff' => [
'asc' => ['reportPercentDiff' =>SORT_ASC ],
'desc' => ['reportPercentDiff' => SORT_DESC],
'default' => SORT_ASC
],
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Then in index.php (view file in which you are having grid view) add lastReportResult and reportPercentDiff in array of all attributes (list of all attributes ob Object model)
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// your other attribute here
'lastReportResult',
'reportPercentDiff',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
...
For more info you can visit Kartik's blog at Yii
Though this is an old thread, stumbled upon this and tried to find other method to achieve sorting of purely calculated field to no avail... and this post unfortunately is not an answer as well... It just that I feel the need to post it here to give a heads up to those that still looking for the solution so as not to scratch their heads when trying the solution given and still fail.
The given example from documentation or referred links as far as I have tested only works if you have a column within the database schema (whether in the main table or the related tables). It will not work if the virtual attribute/calculated field you create is based on calculating (as an example multiplication of 2 column on the table)
e.g:
table purchase: | purchase_id | product_id | quantity |
table product: | product_id | unit_price |
then, if we use a virtual attribute 'purchase_total' for model 'purchase' which is the multiplication of quantity and unit_price (from the join table of purchase and product on product_id), eventually you will hit an error saying 'purchase_total' column can not be found when you tried to sort them using the method discussed so far.