0

I have 3 models—Products, Users, and UserProducts. A UserProduct is an instance of a particular product with a User’s specific price, product code etc for that item. Consequently one Product can have many UserProducts, and through those UserProducts, many Users. These relationships are defined within the Product model:

public function userProducts() {
       return $this->hasMany(UserProduct::class);
}
 
public function users() {
return $this->belongsToMany(User::class, 'user_products');
}

I know that these relationships are working, as in Artisan Tinker I get results when calling either of these as a property.

In my Algolia products index, I want results to contain information about related Users and UserProducts for filtering. Consequently, I’m loading these relationships as recommended in both the Scout Extended and Algolia documentation:

public function toSearchableArray() {
    $this->users;
    $this->userProducts;
    $array = $this->toArray();
     // Apply Scout Extended default transformations:
    $array = $this->transform($array);
    return $array;
}

Everything is pulling through, but for some reason ‘users’ and ‘user_products’ are coming up empty.

Screenshot of Algolia products index

This is made all the more confusing by the fact that UserProducts, which have ‘user’ and 'product' relationships, are successfully loading those relationships prior to them being pulled into my user_products index:

Screenshot of Algolia index

From this, I can only conclude that the issue must lie with how I’ve set up the factory files that populate the database. In the case of UserProducts I’ve used a simple factory:

<?php
 
/** @var \Illuminate\Database\Eloquent\Factory $factory */
 
use App\UserProduct;
use Faker\Generator as Faker;
 
$factory->define(UserProduct::class, function (Faker $faker) {
   return [
       'code' => 'EAN' . $faker->ean8,
       'price' => $faker->randomFloat(2, 5, 1000),
       'product_id' => App\Product::all()->random()->id,
       'user_id' => App\User::where('type', 'supplier')->inRandomOrder()->first()->id
   ];
});

… whereas what happens in the User factory is a bit more complex:

$factory->define(User::class, function (Faker $faker) {
   return [
       'display_name' => $faker->name,
       'email' => $faker->unique()->safeEmail,
       'phone' => $faker->phoneNumber,
       'type'  => array_rand(['supplier', 'buyer']),
       'profile_completed' => true,
       'email_verified_at' => now(),
       'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
       'remember_token' => Str::random(10),
   ];
});
 
$factory->afterCreating(User::class, function ($user, $faker) {
 
   $user->assignRole($user->type);
 
   if($user->type==='supplier') {
       // create five products for them
       $products = factory(App\Product::class, 5)->create();
 
       $products->each(function($product) use ($user) {
 
           $user_product = factory(App\UserProduct::class)->create([
               'product_id' => $product->id,
               'user_id' => $user->id
           ]);
 
       })->each(function($product){
           $product->setUserProductCount();
           $product->update();
       });
 
   }
});

All the changes made in the afterCreating callback are performed successfully. However, no amount of fiddling with this code, adding $product->searchable() etc seems to bring the necessary data through to my products index. Any helpful suggestions would be greatly appreciated.

Delon
  • 255
  • 3
  • 21
  • I've established that it's definitely an issue with the factory definitions, as updating a product instance updates the record for that product in the index with all the necessary information. Still haven't figured out what's wrong with the factory though. – Delon Jul 29 '20 at 11:29

1 Answers1

1

It can be that you forgot to use the touch feature of Laravel on your Product model. See https://www.algolia.com/doc/framework-integration/laravel/indexing/configure-searchable-data/?language=php#updating-relations-when-parentchild-change

Let me know if this solves your issue

chloelbn
  • 26
  • 1
  • Unfortunately using the touch feature does not seem to help my issue. Using it on the 'users' attribute (a belongsToMany relationship) does nothing, whereas using it on the 'userProducts' attribute (a hasMany relationship) causes the seed command to hang indefinitely. – Delon Aug 15 '20 at 06:32
  • Actually, after a bunch of fiddling around this does seem to have been the necessary fix, my results previously were obfuscated by something to do with laravel/artisan/composer's caching behaviour. Indexing is working fine after adding the $touches array. Thanks for your help. – Delon Aug 15 '20 at 09:40