Laravel 5.1 foreign keys in model factory - foreign-keys

How do you define foreign keys in a model factory. For example if I have a organisations table which has a foreign key to the countries table, in my model factory I'm having to define a dummy value for the country id as follows:
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => 197,
];
});
In my organisations table seeder class I am doing the following but the faker object isn't present - do I need to create a new faker object in my seeder class?
use Illuminate\Database\Seeder;
class OrganisationsTableSeeder extends Seeder
{
public function run()
{
$countryIds = Country::lists('id')->all();
factory('App\Organisation', 3)->create([
// override factory default
'country_id' => $faker->randomElement[$countryIds],
]);
}
}
Database seeder class
class DatabaseSeeder extends Seeder
{
public function run()
{
Model::unguard();
$this->call('CountriesTableSeeder');
$this->call('OrganisationsTableSeeder');
Model::reguard();
}
}
Whats the best way to define the foreign keys when defining model factories? Is it possible to omit the country_id from the model factory and add it in the seeder class instead - from the documention it seems you can only override an existing value defined in the model factory but you cant add a new value via the seeder class - correct me if i'm wrong?

I may be a bit late on this one but I was having the same issue, this fixed it for me. You should be able to do
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => factory(App\Country::class)->create()->id,
];
});
and then in your seed you just need to call
factory(App\Organisation::class, 5)->create();
and it will create the countries for you as well.

The way that the Laravel team, Otwell, Stauffer et.al., suggest is like this.
Testing > Adding Relations To Models
Adding relationships to your models
ModelFactory.php
$factory->define(App\Organisation::class, function ($faker) {
return [
'name' => $faker->company,
'country_id' => 197,
];
});
$factory->define(App\Country::class, function ($faker) {
return [
'name' => $faker->country,
];
});
seeder
$organisations = factory('App\Organisation', 3)
->create()
->each(function($$organisation) {
$organisation->relatedItems()->save(factory('App\Country')->make());
});

Related

how to group this relationship in the laravel?

I am trying to group users by date that is in the relationship accesses and page this result.
Model User
public function acessos()
{
return $this->hasMany(Acessos::class,'id_usuario');
}
Model Acessos
public function user()
{
return $this->belongsToMany(User::class,'id_usuario');
}
Livewire component
$data = User::query()
->search($this->search)
->with('acessos')
->has('acessos')
->paginate($this->perPage);
return view('livewire.relatorios.acessos.acessos-component',[
'data' => $data
]);
Remove the user() method from Acessos Model and change the hasMany() method to belongsToMany in User model's acessos method. This Will solve your problem.
Remove this:
public function user()
{
return $this->belongsToMany(User::class,'id_usuario');
}
And change this:
public function acessos()
{
return $this->belongsToMany(Acessos::class,'id_usuario');
}

Customising Laravel 5.5 Api Resource Collection pagination

I have been working with laravel api resource. By default laravel provides links and meta as shown below.
"links": {
"first": "https://demo.test/api/v2/taxes?page=1",
"last": "https://demo.test/api/v2/taxes?page=4",
"prev": null,
"next": "https://demo.test/api/v2/taxes?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 4,
"path": "https://demo.test/api/v2/taxes",
"per_page": 2,
"to": 2,
"total": 8
}
But I don't want this, insted i want something like
"pagination": {
"total": 8,
"count": 8,
"per_page": 25,
"current_page": 1,
"total_pages": 1
}
I'm able to get this info but if I do return TaxResource::collection($taxes);, I won't get this. Even I have custom collection method
public static function collection($resource)
{
$resource->pagination = [
'total' => $resource->total(),
'count' => $resource->count(),
'per_page' => $resource->perPage(),
'current_page' => $resource->currentPage(),
'total_pages' => $resource->lastPage()
];
return parent::collection($resource);
}
It is not giving what I want. But if I reference through (TaxResource::collection($taxes))->pagination; I'm able to get that. But I want it to be returned when I do return TaxResource::collection($taxes);
I was interested in your question and spent some time resolving it. I guess there are a lot of work to be done to improve Eloquent: API Resources' functionality in the future.
In order to resolve it I must use Resource Collections instead of Resources:
However, if you need to customize the meta data returned with the collection, it will be necessary to define a resource collection
php artisan make:resource Tax --collection
or
php artisan make:resource TaxCollection
Route:
Route::get('/get-taxes', function () {
$taxes = Taxes::paginate();
return new TaxCollection($taxes);
});
TaxCollection.php:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TaxCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection,
'pagination' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage()
],
];
}
// Using Laravel < 5.6
public function withResponse($request, $response)
{
$originalContent = $response->getOriginalContent();
unset($originalContent['links'],$originalContent['meta']);
$response->setData($originalContent);
}
// Using Laravel >= 5.6
public function withResponse($request, $response)
{
$jsonResponse = json_decode($response->getContent(), true);
unset($jsonResponse['links'],$jsonResponse['meta']);
$response->setContent(json_encode($jsonResponse));
}
}
This solve the problem but now there are new one:
Unlike Resources I don't know how to modify toArray fields in Resource Collections, the manual shows only example with 'data' => $this->collection where we send not modified collection (Resource Collections allows us change meta data). So If we use just Resource then we can modify collection data but not meta data.
The accepted answer did not work for me (in Laravel 5.6), but I found a better way IMHO.
Save the pagination informations in your ResourceCollection constructor and replace the Paginator resource with the underlying Collection.
TaxCollection.php:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TaxCollection extends ResourceCollection
{
private $pagination;
public function __construct($resource)
{
$this->pagination = [
'total' => $resource->total(),
'count' => $resource->count(),
'per_page' => $resource->perPage(),
'current_page' => $resource->currentPage(),
'total_pages' => $resource->lastPage()
];
$resource = $resource->getCollection();
parent::__construct($resource);
}
public function toArray($request)
{
return [
'data' => $this->collection,
'pagination' => $this->pagination
];
}
}
So I've discovered that in PHP you can actually call a grandparent function without reflection or other workarounds.
Given that TaxCollection extends ResoureCollection, which in turn extends JsonResource we can actually bypass the ResourceCollection method that handles the pagination.
class TaxCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection,
'pagination' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage()
],
];
}
public function toResponse($request)
{
return JsonResource::toResponse($request);
}
}
the toResponse method call is NOT static, but instead calling the grandparent JsonResource::toResponse method, just as parent::toResponse would call the ResourceCollection toResponse(..) instance method.
This will remove all extra pagination fields from the JSON response (links, meta, etc) and allow you to customize the response as you'd like in toArray($request)
you could also extends JsonResource, AnonymousResourceCollection, ResourceCollection and finally PaginatedResourceResponse
#yrv16 Laravel 5.6 version:
public function withResponse($request, $response)
{
$jsonResponse = json_decode($response->getContent(), true);
unset($jsonResponse['links'],$jsonResponse['meta']);
$response->setContent(json_encode($jsonResponse));
}
JsonResource class comes with an additional() method which lets you specify any additional data you’d like to be part of the response when working with a resource:
Route::get('/get-taxes', function () {
$taxes = Taxes::paginate();
return new TaxCollection($s)->additional([
'pagination' => [
'total' => $taxes->total,
...
]
]);
});

Retrieve a taxonomy term in the buildrow function of a drupal 8 custom entity

I have built a custom entity that works well. One of my fields is a taxonomy but I can not retrieve the name of the term in the buildRow(EntityInterface $entity) function which displays my records.
For a simple string field I do: $row['foo'] = $entity->foo->value;
How to do a taxonomy term that is an entity_reference: $row['bar'] = $entity->BAR_TERM_NAME;
Thank you for your help.
To work as requested you need 3 things:
Implements an entity_reference field in your custom Entity.
Add a getter methode for you field.
Retrieve your field in your custom ListBuilder -> buildRow().
Check the Drupal 8 documentation about FieldTypes, FieldWidgets and FieldFormatters.
Implements an entity_reference field
Your field foo in your Entity should be generated using the entity_reference field type.
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
// Some code ...
$fields['foo'] = BaseFieldDefinition::create('entity_reference')
->setLabel($this->t('Foo field'))
->setDescription($this->t('The Foo field.'))
->setSetting('target_type', 'taxonomy_term')
->setSetting('handler', 'default')
->setSetting('handler_settings', ['target_bundles' => ['vocabulary_id' => 'vocabulary_id']])
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'vocabulary_id',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'options_select',
'weight' => 40,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
// Some code ...
}
You should then replace the 3 vocabulary_id by the vocabulary that you wanna links.
Add a getter
In the same Class as your baseFieldDefinitions.
// Some code ...
public function getFoo() {
return $this->get('foo')->value;
}
// Some code ...
Retrieve the field
In your ListBuilder Class.
public function buildRow(EntityInterface $entity) {
// Some code ...
$row['foo'] = $entity->getFoo();
// Some code ...
}
Hopes it will help you !

phpunit test laravel creating object and relationship

This question is one in a series I seem to be generating as I slowly pick my way through learning testing.
I have a book model and a ticketaudit model. They have a relationship one to many. When a book is created a function should also create a range of tickets (for audit).
I want my test to make sure the ticketAudit model is being created and the association being made using the eloquent ORM within laravel.
my class so far:
Class TicketCreator implements TicketCreatorInterface {
protected $ticket;
public function __construct(TicketAudit $ticketAudit)
{
//dd($ticketAudit);
$this->ticket = $ticketAudit;
}
public function createTicket($input, $book) {
$counter = $input['start'];
while($counter <= $input['end']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
}
and my attempts at a test:
public function testCreateCreatesTickets() {
//arrange
$book = FactoryMuff::create('Book');
$aTicket = FactoryMuff::create('TicketAudit');
$ticketAudit = new TicketAudit;
$ticketCreator = new TicketCreator($ticketAudit);
//act
$response = $ticketCreator->createTicket(array('start'=>1000, 'end'=>1001), $book);
// Assert...
$this->assertEquals(true, $response);
}
However when I run this test I get the error:
Integrity constraint violation: 19 ticket_audits.ticketnumber may not be NULL
For some reason the model is not being created with the values I pass to it. I've checked in the function that the object exists and also the values are being created correctly in the array but it doesnt work.
Is this unique to testing?
I am creating an sqlite in memory database for this test.
Any help appreciated
Crikey this decision to start testing is a bit of a nightmare
Thanks to Manuel's request to post the TicketAudit class I noticed my model extended Eloquent. I had recently added Ardent and should have extended Ardent so the error lay in the model!
Revised corrected model :
use LaravelBook\Ardent\Ardent;
class TicketAudit extends Ardent {
protected $guarded = array();
public $autoHydrateEntityFromInput = true;
public $autoPurgeRedundantAttributes = true;
public static $rules = array(
'status' => 'required',
'ticketnumber' => 'required'
);
public static $factory = array(
'ticketnumber' => '1000',
'status' => 'unused',
);
public function book() {
return $this->belongsTo('Book');
}
}
Thank you for the sign post

How can I clone an Ember Data record, including relationships?

I've figured out that I can clone an Ember Data record and copy its Attributes, but none of the belongsTo/hasMany relationships are cloned. Can I do this somehow if I don't know what relationships would be possible, going off of the relationships that exist?
For reference, here is what I've got that will clone an Ember Data record's attributes:
var attributeKeys = oldModel.get('constructor.attributes.keys.list');
var newRecord = this.get('store').createRecord(oldModel.constructor.typeKey);
newRecord.setProperties(oldModel.getProperties(attributeKeys));
A few improvements to Daniel's answer that allows providing overrides, and clears the id for new records, to be safe. Also I prefer not to call save within the clone method, but leave it up to the caller.
DS.Model.reopen({
clone: function(overrides) {
var model = this,
attrs = model.toJSON(),
class_type = model.constructor;
var root = Ember.String.decamelize(class_type.toString().split('.')[1]);
/*
* Need to replace the belongsTo association ( id ) with the
* actual model instance.
*
* For example if belongsTo association is project, the
* json value for project will be: ( project: "project_id_1" )
* and this needs to be converted to ( project: [projectInstance] )
*/
this.eachRelationship(function(key, relationship) {
if (relationship.kind == 'belongsTo') {
attrs[key] = model.get(key);
}
});
/*
* Need to dissociate the new record from the old.
*/
delete attrs.id;
/*
* Apply overrides if provided.
*/
if (Ember.typeOf(overrides) === 'object') {
Ember.setProperties(attrs, overrides);
}
return this.store.createRecord(root, attrs);
}
});
Here is a clone function that I use. Takes care of the belongs to associations.
DS.Model.reopen({
clone: function() {
var model = this,
attrs = model.toJSON(),
class_type = model.constructor;
var root = Ember.String.decamelize(class_type.toString().split('.')[1]);
/**
* Need to replace the belongsTo association ( id ) with the
* actual model instance.
*
* For example if belongsTo association is project, the
* json value for project will be: ( project: "project_id_1" )
* and this needs to be converted to ( project: [projectInstance] )
*
*/
this.eachRelationship(function(key, relationship){
if (relationship.kind == 'belongsTo') {
attrs[key] = model.get(key)
}
})
return this.store.createRecord(root, attrs).save();
}
})
There's an addon called ember-cli-copyable that according to its description:
Deeply copies your records including their relations. The mixin is smart enough to resolve not loaded relations and is configurable to what should be shallow/deeply copied or excluded entirely.
Here is the simple way to clone your Ember Model with relationships.
working fine.
Create a Copyable mixin like,
import Ember from 'ember';
export default Ember.Mixin.create(Ember.Copyable, {
copy(deepClone) {
var model = this, attrs = model.toJSON(), class_type = model.constructor;
var root = Ember.String.decamelize(class_type.toString().split(':')[1]);
if(deepClone) {
this.eachRelationship(function(key, relationship){
if (relationship.kind == 'belongsTo') {
attrs[key] = model.get(key).copy(true);
} else if(relationship.kind == 'hasMany' && Ember.isArray(attrs[key])) {
attrs[key].splice(0);
model.get(key).forEach(function(obj) {
attrs[key].addObject(obj.copy(true));
});
}
});
}
return this.store.createRecord(root, attrs);
}
});
Add the mixin in your model,
Note: If you want to clone your child model then, you need to include the mixin in child model as well
USAGE:
With relationship : YOURMODEL.copy(true)
Without relationship : YOURMODEL.copy()