Laravel DataTables Service implementation and Joins - php

Can't figure out simple task: join 1 table and add column. There is no useful documentation about service implementation here: DataTables as a Service Implementation
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select($this->getColumns());
return $this->applyScopes($query);
}
protected function getColumns()
{
return [
'manufacturers.id',
];
}
Above will trigger bizarre error
Requested unknown parameter 'manufacturers.id' for row 0, column 0
Tried many variations like:
return [
'id',
];
Above will trigger Column 'id' in field list is ambiguous
Another one was:
return [
[
'name' => 'id',
'data' => 'id'
]
];
this will result in: strtolower() expects parameter 1 to be string, array given
And so on and so forth. Maybe some one just can give basic join example using Service implementation?
System details
Operating System OSX
PHP Version 7.2
Laravel Version 5.5
Laravel-Datatables Version 8.0
Update #1
This one seams to be closest to working solution:
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select( $this->getColumns() );
return $this->applyScopes($query);
}
and
protected function getColumns()
{
return [
'technika.id',
'manufacturers.title'
];
}
But I'm getting Requested unknown parameter 'technika.id' for row 0, column 0.
However XHR response seams to be ok, I can see correct ata coming from backend.

Solved problme by doing this:
protected function getColumns()
{
return [
[ 'data' => 'id', 'name' => 'technika.id', 'title' => 'ID' ],
[ 'data' => 'title', 'name' => 'manufacturers.title', 'title' => 'Manufacturer' ]
];
}
public function query()
{
$query = Technika::query()
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->select( collect($this->getColumns())->pluck('name')->toArray() );
return $this->applyScopes($query);
}
getColumns method is used in query() and in html() and they both expect different type of array format. So easest way is to extract name key ant put it to query() select method.

hope this will works for you if not then let me know
$query = Technika::query()
->select($this->getColumns())
->join('manufacturers','technika.manufacturer_id','=','manufacturers.id')
->get();
return $this->applyScopes($query);
protected function getColumns()
{
return 'manufacturers.id'
}

Related

Laravel Factory: Manual Increment of Column

For the following factory definition, the column order needs to be sequential. There is already a column id that is auto-incremented. The first row's order should start at 1 and each additional row's order should be the next number (1,2,3, etc.)
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => (App\AliasCommand::count()) ?
App\AliasCommand::orderBy('order', 'desc')->first()->order + 1 : 1
];
});
It should be setting the order column to be 1 more than the previous row, however, it results in all rows being assigned 1.
Here's something that might work.
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
static $order = 1;
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $order++
];
});
It just keeps a counter internal to that function.
Update:
Laravel 8 introduced new factory classes so this request becomes:
class AliasCommandFactory extends Factory {
private static $order = 1;
protected $model = AliasCommand::class;
public function definition() {
$faker = $this->faker;
return [
'user_id' => User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => self::$order++
];
}
}
The answer by #apokryfos is a good solution if you're sure the factory model generations will only be run in sequential order and you're not concerned with pre-existing data.
However, this can result in incorrect order values if, for example, you want to generate models to be inserted into your test database, where some records already exist.
Using a closure for the column value, we can better automate the sequential order.
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => function() {
$max = App\AliasCommand::max('order'); // returns 0 if no records exist.
return $max+1;
}
];
});
You almost had it right in your example, the problem is that you were running the order value execution at the time of defining the factory rather than the above code, which executes at the time the individual model is generated.
By the same principle, you should also enclose the user_id code in a closure, otherwise all of your factory generated models will have the same user ID.
To achieve true autoIncrement rather use this approach:
$__count = App\AliasCommand::count();
$__lastid = $__count ? App\AliasCommand::orderBy('order', 'desc')->first()->id : 0 ;
$factory->define(App\AliasCommand::class,
function(Faker\Generator $faker) use($__lastid){
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $faker->unique()->numberBetween($min=$__lastid+1, $max=$__lastid+25),
/* +25 (for example here) is the number of records you want to insert
per run.
You can set this value in a config file and get it from there
for both Seeder and Factory ( i.e here ).
*/
];
});
In Laravel 9 (and possibly some earlier versions?), there's a pretty clean way to make this happen when you're creating models (from the docs):
$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['order' => $sequence->index])
->create();
If you'd like to start with 1 instead of 0:
$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['order' => $sequence->index + 1])
->create();
The solution also solves already data on table conditions:
class UserFactory extends Factory
{
/**
* #var string
*/
protected $model = User::class;
/**
* #var int
*/
protected static int $id = 0;
/**
* #return array
*/
public function definition()
{
if ( self::$id == 0 ) {
self::$id = User::query()->max("id") ?? 0;
// Initialize the id from database if exists.
// If conditions is necessary otherwise it would return same max id.
}
self::$id++;
return [
"id" => self::$id,
"email" => $this->faker->email,
];
}
}

Yii Framework - ActiveRecord not returning data of join table

Yii Framework ist really great, but I encountered a problem when using Active Record for querying data from MySQL database.
When I join 2 tables ('building' and 'building_info') in my Function "get" in model Building, there will be no data returned from my second table. If I execute the same query with Query Class, rows from both table will be returned. With Active Record I get only data from table 'building'.
Model Building:
...
// Setting Relation
public function getBuildingInfos()
{
return $this->hasMany(BuildingInfo::className(), ['BuildingID' => 'ID']);
}
// Get all buildings
public function get() {
$building = Building::find()
->joinWith('buildingInfos')
->where(['building_info.langID' => 1])
->all();
return $building;
}
// Attributes
public function attributeLabels()
{
return [
'ID' => 'ID',
'NameBreak' => 'Name Break',
'Tileset' => 'Tileset',
'TilesetPosition' => 'Tileset Position',
...
]
}
...
Model BuildingInfo:
...
public function attributeLabels()
{
return [
'buInfoID' => 'Bu Info ID',
'BuildingID' => 'Building ID',
'langID' => 'Lang ID',
'Name' => 'Name',
'ShortDesc' => 'Short Desc',
'ShortDescDisabled' => 'Short Desc Disabled',
];
}
public function getBuilding()
{
return $this->hasOne(Building::className(), ['ID' => 'BuildingID']);
}
...
Do you know how to solve this problem?
Thanks
Kevin
Ciao Kevin :) are you using yii1 or yii2?
In 'getBuildingInfos', the table is linked by BuidingID, in your get function, you are linking it by building_info.langID.. Have you already verified that your join works in sql?

Using Fractal's $defaultIncludes

I am trying to use a $defaultIncludes() and am getting an exception --
ErrorException in ViewoptionTransformer.php line 8:
Argument 1 passed to App\Transformers\ViewoptionTransformer::transform() must be an instance of App\Viewoption, boolean given
Following the tutorial (http://laravelista.com/build-an-api-with-lumen-and-fractal/) except I am using Laravel 5.1 not Lumen:
in User model, I have the hasOne relationship with Viewoption called viewoptions
In the UsersController, I eager load viewoptions
public function index(Manager $fractal, UserTransformer $userTransformer)
{
$records = User::with(['locations', 'viewoptions'])->get();
$collection = new Collection($records, $userTransformer);
$data = $fractal->createData($collection)->toArray();
return $this->respondWithCORS($data);
}
In the UserTransformer, I have the $defaultInclude and the includes method
protected $defaultIncludes = ['viewoptions'];
`public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->name,
'is_active' => (boolean)$user->is_active,
'is_admin' => (boolean)$user->is_admin,
'is_manager' => (boolean)$user->is_manager,
'role_id' => (integer) $user->role_id,
'email' => $user->email,
'phone' => $user->phone,
'full_sidebar' => (boolean)$user->full_sidebar
];
}
public function includeViewoptions(User $user)
{
$viewoptions = $user->viewoptions;
return $this->collection($viewoptions, new ViewoptionTransformer);
}`
Have a ViewoptionTransformer
`
use App\Viewoption;
use League\Fractal\Resource\Collection;
use League\Fractal\TransformerAbstract;
class ViewoptionTransformer extends TransformerAbstract {
public function transform(Viewoption $item)
{
//return $item;
return [
'id' => $item->id,
'user_id' => $item->user_id,
'voAgency' => (boolean)$item->voAgency,
'voBalanceDue' => (boolean)$item->voBalanceDue,
'voCloseDate' => (boolean)$item->voCloseDate,
'voCommitTotal' => (boolean)$item->voCommitTotal,
'voDistributor' => (boolean)$item->voDistributor,
'voDueDate' => (boolean)$item->voDueDate,
'voFeePercentage' => (boolean)$item->voFeePercentage,
'voRegion' => (boolean)$item->voRegion,
'voSeason' => (boolean)$item->voSeason,
];
}
}`
Worked with these and slight variations of these throughout the day yesterday and I can't rid myself of that exception.
Not all of your users.id corresponds to some viewoptions.user_id.
Just check it:
$records = User::with(['locations', 'viewoptions'])->get();
dd($records);
some viewoptions will be null or false or just undefined
Instead of using collection use item like so
public function includeViewoptions(User $user){
$viewoptions = $user->viewoptions;
return $this->item($viewoptions, new ViewoptionTransformer);
}`
I had a similar issue today, all my other uses of transformers had been with hasMany relationships. I was always instantiating the transformer with a collection of objects.
However, when using a transformer with a belongsTo relationship and instantiating the transformer with only one object (similar to how you are passing only one object from a hasOne relationship) I would get the same boolean given error.
In the end I solved the issue by using 'item' instead of 'collection' when instantiating the transformer.
Within your includeViewoptions function
Instead of using
return $this->collection($viewoptions, new ViewoptionTransformer);
try
return $this->item($viewoptions, new ViewoptionTransformer);

yii2:data of related model in Gridview

I have two models namely MedicineRequestEntry and MedicineRequest. MedicineRequestEntry is related to MedicineRequest via
public function getMedicineRequests()
{
return $this->hasMany(MedicineRequest::className(),
['medicine_request_entry_id' => 'id']);
}
Now in grid-view of MedicineReuestEntry I am trying to pull the data from MedicineRequest Model using the relation using two alternative ways
like
[
'attribute' => 'is_delivered',
'value'=> 'medicineRequests.is_delivered'
],
In this method I am getting the value as not set.
and another method:
[
'attribute' => 'is_delivered',
'value'=> '$data->medicineRequests->is_delivered'
],
In this method I am getting the error like:
Getting unknown property: app\models\MedicineRequestEntry::$data->medicineRequests->is_delivered
Now I need some help, what I am doing wrong here.
Thank you.
You should use a callback function, see the guide:
[
'value' => function ($data) {
$str = '';
foreach($data->medicineRequests as $request) {
$str .= $request->is_delivered.',';
}
return $str;
},
],
Or for the first result of the array:
[
'value' => function ($data) {
return $data->medicineRequests[0]->is_delivered;
},
],

Can't transform results of Eloquent query

I'm building an API in Laravel to learn how to do such a thing. I'm following a Laracasts course to do this, but I'm having some troubles with the parts I want to do for myself.
Currently, I have this function in my controller. It fetches data from two tables and then returns it.
public function lesson($userid)
{
$lessons = DB::table('lessons')
->join('userlessons', 'lessons.id', '=', 'userlessons.lessonsid')
->select('lessons.name', 'lessons.seen')
->where('userlessons.userid','=', $userid)
->get();
return $this->respondWithPagination($lessons, [
'data' => $this->LessonTransformer->transformCollection($lessons)
]);
}
And LessonTransformer is this:
class LessonTransformer extends Transformer
{
public function transformCollection($items)
{
return array_map([$this, 'transform'], $items);
}
public function transform($item)
{
return [
'name' => $item['name'],
'seen' => (bool) $item['seen']
];
}
}
I tried a lot of solutions, some smart, some stupid. But I keep getting this error: Cannot use object of type stdClass as array
If you get such error, probably you need to change:
return [
'name' => $item['name'],
'seen' => (bool) $item['seen']
];
into:
return [
'name' => $item->name,
'seen' => (bool) $item->seen
];
but you haven't showed in which line error appear so it's only a guess

Categories