Lighthouse Nested Mutations FK Field not being filled - graphql

I'm having some issues wrapping my head around nested mutations with Lighthouse.
I've got two models that are related: Ams\Account hasMany Ams\Contact. This is the Ams\Contact model:
namespace App\Models\Ams;
use Eloquent as Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Contact extends Model
{
use SoftDeletes;
public $table = 'ams_contacts';
protected $dates = ['deleted_at'];
public $fillable = [
'ams_account_id',
'forename',
'surname',
'active',
'email'
];
protected $casts = [
'forename' => 'string',
'surname' => 'string',
'active' => 'boolean',
'email' => 'string'
];
public static $rules = [
'ams_account_id' => 'required',
'forename' => 'required',
'surname' => 'required',
'email' => 'required|email'
];
public function amsAccount(): BelongsTo
{
return $this->belongsTo(\App\Models\Ams\Account::class, 'ams_account_id', 'id');
}
}
My GraphQL schema file looks like this:
"A datetime string with format `Y-m-d H:i:s`, e.g. `2018-01-01 13:00:00`."
scalar DateTime #scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")
"A date string with format `Y-m-d`, e.g. `2011-05-23`."
scalar Date #scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")
type Query {
amsAccounts: [AmsAccount!]! #paginate(defaultCount: 10 model: "App\\Models\\Ams\\Account")
amsAccount(id: ID #globalId): AmsAccount #find(model: "App\\Models\\Ams\\Account")
amsContacts: [AmsContact!]! #paginate(defaultCount: 10 model: "App\\Models\\Ams\\Contact")
amsContact(id: ID #globalId): AmsContact #find(model: "App\\Models\\Ams\\Contact")
}
type Mutation {
createAmsAccount(
input: CreateAmsAccountInput! #spread
): AmsAccount #create(model: "App\\Models\\Ams\\Account")
updateAmsAccount(
input: UpdateAmsAccountInput! #spread
): AmsAccount #update(model: "App\\Models\\Ams\\Account")
deleteAmsAccount(
id: ID! #rules(apply: ["required"])
): AmsAccount #delete(model: "App\\Models\\Ams\\Account")
createAmsContact(
input: CreateAmsContactInput! #spread
): AmsContact #create(model: "App\\Models\\Ams\\Contact")
updateAmsContact(
input: UpdateAmsContactInput! #spread
): AmsContact #update(model: "App\\Models\\Ams\\Contact")
deleteAmsContact(
id: ID! #rules(apply: ["required"])
): AmsContact #delete(model: "App\\Models\\Ams\\Contact")
}
type AmsAccount #node(namespace: "App\\Models\\Ams\\Account") {
id: ID! #globalId
name: String!
url: String
telephone: String!
created_at: DateTime!
updated_at: DateTime!
}
input CreateAmsAccountInput {
name: String!
url: String
telephone: String!
}
input UpdateAmsAccountInput {
id: ID!
name: String!
url: String
telephone: String!
}
input UpsertAmsAccountInput {
id: ID!
name: String!
url: String
telephone: String!
}
type AmsContact #node(namespace: "App\\Models\\Ams\\Contact") {
id: ID! #globalId
forename: String!
surname: String!
active: Boolean
email: String!
amsAccount: AmsAccount! #belongsTo
created_at: DateTime!
updated_at: DateTime!
}
input CreateAmsContactInput {
forename: String!
surname: String!
active: Boolean
email: String!
amsAccount: CreateAmsAccountBelongsTo
}
input UpdateAmsContactInput {
id: ID!
forename: String!
surname: String!
active: Boolean
email: String!
amsAccount: UpdateAmsAccountBelongsTo
}
input UpsertAmsContactInput {
id: ID!
forename: String!
surname: String!
active: Boolean
email: String!
amsAccount: UpsertAmsAccountBelongsTo
}
input CreateAmsAccountBelongsTo {
connect: ID
create: CreateAmsAccountInput
update: UpdateAmsAccountInput
upsert: UpsertAmsAccountInput
}
input UpdateAmsAccountBelongsTo {
connect: ID
create: CreateAmsAccountInput
update: UpdateAmsAccountInput
upsert: UpsertAmsAccountInput
disconnect: Boolean
delete: Boolean
}
input UpsertAmsAccountBelongsTo {
connect: ID
create: CreateAmsAccountInput
update: UpdateAmsAccountInput
upsert: UpsertAmsAccountInput
disconnect: Boolean
delete: Boolean
}
When I try and run the following mutation in the playground:
mutation {
createAmsContact(
input: {
forename: "Jane"
surname: "Doe"
active: true
email: "jd#example.com"
amsAccount: {
connect: 1
}
}
) {
id
forename
surname
amsAccount: id
}
}
I get an error about the following error message:
"debugMessage": "SQLSTATE[HY000]: General error: 1364 Field 'ams_account_id' doesn't have a default value (SQL: insert into `ams_contacts` (`forename`, `surname`, `active`, `email`, `updated_at`, `created_at`) values (Jane, Doe, 1, jd#example.com, 2020-04-12 15:31:52, 2020-04-12 15:31:52))"
I've tried a number of fixes and suggestions from around the web, and as far as I can see my schema follows the defined format from the documentation, but I cannot fathom out why it's not working.
I've also confirmed that the simple mutation for the account work fine. I also get a similar error message when I try to create a new account at the same time as the contact.
My inclination is that it's to do with the namespaces of the different models, but I can't imagine I'm the first to have this requirement, nor can I find any references to how to overcome this.
Any and all help will be greatly appreciated.

So I wasn't actually a million miles from the mark with my Namespace problem.
The problem lies with the Model file, specifically here:
public function amsAccount(): BelongsTo
{
return $this->belongsTo(\App\Models\Ams\Account::class, 'ams_account_id', 'id');
}
The issue is that the BelongsTo had not been specified in the use clauses at the start of the model class. As such the return class type could not be detected properly.
As such it was necessary to add the following to the use clauses at the top of the file:
use Illuminate\Database\Eloquent\Relations\BelongsTo;
This applies to other relationship types.
This is what the Documentation states, but is so subtle that it didn't click.
RTFM.
Thanks for all who looked and hope this helps someone else in the future.

Related

Eloquent Polymorphic MorphMany with children

I have 3 models (User, Post, and Comment).
User model:
public function posts() // A user can have many posts
{
return $this->hasMany('App\Post', 'from_id');
}
Post model:
protected $with = [ // I'm using with() here
'from',
'for',
'comments',
];
public function from() // Post owner
{
return $this->belongsTo(User::class, 'from_id');
}
public function for() // To whom this post is addressed
{
return $this->belongsTo(User::class, 'for_id');
}
public function comments() // All comments for this post
{
return $this->morphMany(Comment::class, 'commentable');
}
Post migration:
$table->id();
$table->string('uuid');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('for_id')->references('id')->on('users');
$table->unsignedBigInteger('for_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Comment model:
protected $with = [ // I'm also using with() here
'from',
'children',
];
public function from() // Comment owner
{
return $this->belongsTo(User::class, 'from_id', 'id');
}
public function commentable()
{
return $this->morphTo();
}
public function children() // child comments for this comment (?) Not sure how it's working or not
{
return $this->hasMany(Comment::class, 'parent_id');
}
Comment migration:
$table->id();
$table->string('uuid');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->foreign('from_id')->references('id')->on('users');
$table->unsignedBigInteger('from_id');
$table->foreign('parent_id')->references('id')->on('comments');
$table->unsignedBigInteger('parent_id')->nullable();
$table->text('body');
$table->timestamps();
$table->softDeletes();
Note that I have two dummy users in my Users table.
Then let's say we have two records inside our Posts table:
|------------------------------------------------------|
| id | uuid | from_id | for_id | body | ... |
|------------------------------------------------------|
| 1 | .... | 1 | null | ... | ... |
| 2 | .... | 1 | null | ... | ... |
So let's create a comment for id #1:
php artisan tinker
>> $post = App\Post::first();
>> $post->comments()->create(['from_id' => 2, 'body' => 'Test comment']);
>> App\Comment {#3744
from_id: 2,
body: "Test comment",
commentable_id: 1,
commentable_type: "App\Post",
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
updated_at: "2020-09-01 15:00:38",
created_at: "2020-09-01 15:00:38",
id: 1,
}
Now let's see the first posts with comments (I'm using protected $with in my Post model):
php artisan tinker
>> $post = App\Post::first();
>> App\Post {#4030
id: 1,
uuid: "e9503551-99ac-495f-902e-b505408ab9ef",
from_id: 1,
for_id: null,
body: "Vero vel officia qui et. Veritatis laudantium itaque nisi sint repellendus laborum. Nihil at aliquam alias in.",
created_at: "2020-09-01 14:59:11",
updated_at: "2020-09-01 14:59:11",
deleted_at: null,
comments: Illuminate\Database\Eloquent\Collection {#4039
all: [
App\Comment {#4049
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4061
id: 2,
name: "Prof. Runte Jr.
email: "abigail#example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4040
all: [],
},
},
],
},
}
We can see the first post now has a comment, alright.
Now I want to create a child comment for the first comment inside the post before:
php artisan tinker
// get the first post, and then get the first comment of it
>> $post = App\Post::first()->comments()->first();
>> App\Comment {#4060
id: 1,
uuid: "68bc8dbd-9769-44d7-8139-3e4e14d3df4f",
commentable_id: 1,
commentable_type: "App\Post",
from_id: 2,
parent_id: null,
body: "Test comment",
created_at: "2020-09-01 15:00:38",
updated_at: "2020-09-01 15:00:38",
deleted_at: null,
from: App\User {#4073
id: 2,
name: "Prof. Runte Jr.
email: "abigail#example.test",
...
},
children: Illuminate\Database\Eloquent\Collection {#4030
all: [],
},
}
// Now we want to create child comment
// Get the first post
>> $post = App\Post::first()
// Get the first comment from it
->comments()->first()
// Get children relationship (IDK if this the right words to put it) and create a child comment
->children()->create(['from_id' => 1, 'body' => 'Testing child comment']);
This is what tinker returns:
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field 'commentable_id' doesn't have a default value (SQL: insert into comments (from_id, body, parent_id, uuid, updated_at, created_at) values (1, Testing child comment, 1, aa3d624f-3984-438c-adb0-26086459de33, 2020-09-01 15:38:36, 2020-09-01 15:38:36))'
So my question is:
How to make a child comment inside polymorphic relationship (morphMany())? I already set the column for parent_id which belongs to comment id.
As comments() return collection of comments. So, You should use this one like:
$post = App\Post::first();
foreach($post->comments() as $comment){
$comment->children()->create([
'from_id' => 1,
'body' => 'Testing child comment'
]);
}
You can also use save() method as:
$post = App\Post::first();
$comment = new App\Comment([
'from_id' => 1,
'body' => 'Testing child comment'
]);
$child = $post->comments()->first()->save($comment);
And You have to change in your App\Comment method children() with this :
public function children()
{
return $this->morphMany(Comment::class, 'commentable');
}

Laravel Lighthouse - Adding Types Programmatically using TypeRegistry. How exactly?

I've been unsuccessfully trying this part in the documentation and have not found any good reference on it on the internets. I want to dynamically add schema types and thought that adding them through the typeregistry in GraphQLServiceProvider would be it but when I try to use the type in graphql-playground, it shows a Type can not be found on schema error. It seems that the type I registered isn't loaded into the schema which I don't know how to do.
Things I've tried:
Added the provider to my app config.
Ran "composer dump-autoload"
Cleared cache and config.
Here's my code for GraphQLServiceProvider
<?php
declare(strict_types=1);
namespace App\Providers;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\ServiceProvider;
use GraphQL\Type\Definition\ObjectType;
use Nuwave\Lighthouse\Schema\TypeRegistry;
class GraphQLServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #param TypeRegistry $typeRegistry
*
* #return void
*/
public function boot(TypeRegistry $typeRegistry): void
{
$typeRegistry->register(
new ObjectType([
'name' => 'Comment',
'fields' => function() use ($typeRegistry): array {
return [
'id' => [
'type' => Type::id()
],
'content' => [
'type' => Type::string()
],
'date_created' => [
'type' => Type::string()
],
'updated_at' => [
'type' => Type::string()
]
];
}
])
);
}
}
and here's my schema
"A datetime string with format `Y-m-d H:i:s`, e.g. `2018-01-01 13:00:00`."
scalar DateTime #scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")
"A date string with format `Y-m-d`, e.g. `2011-05-23`."
scalar Date #scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")
type Query {
users: [User!]! #paginate(defaultCount: 10)
user(id: ID #eq): User #find
posts: [Post!]! #all
comments: [Comment!]! #all
}
type User {
id: ID!
name: String!
email: String!
created_at: DateTime!
updated_at: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
created_at: DateTime!
updated_at: DateTime!
}
Did you register GraphQLServiceProvider? All service providers are registered in config/app.php, add your provider to the array like:
'providers' => [
// Other Service Providers
App\Providers\GraphQLServiceProvider::class,
],
I just tried you peace of code in my project, and it works. So you code is actually fine.
Please check you lighthouse config, probably you have cache enabled. You can enable/disable cache with LIGHTHOUSE_CACHE_ENABLE environment variable (true/false).
If you use lighthouse cache (like in production), don't forget to clear lighthouse cache after changes with php artisan lighthouse:clear-cache
I hope it helps
An alternative way to inject in the schema is like this:
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Modules\AccessControl\Entities\Permission;
use Nuwave\Lighthouse\Events\ManipulateAST;
use Nuwave\Lighthouse\Schema\AST\PartialParser;
use Nuwave\Lighthouse\Schema\Factories\DirectiveFactory;
class GraphQLEnumPermissionServiceProvider extends ServiceProvider
{
public function boot(DirectiveFactory $directiveFactory, Dispatcher $dispatcher): void
{
if (Schema::hasTable(with(new Permission)->getTable())) {
$permissions = Permission::all();
}
else {
$permissions = (object) [ (object) ['id' => '0', 'ui_name' => 'bar']]; // to pass the schema validation when is empty
}
// we need to check if we have permissions in the system
$dispatcher->listen(
ManipulateAST::class,
function (ManipulateAST $manipulateAST) use($permissions): void {
$manipulateAST->documentAST
->setTypeDefinition(
static::createEnumType(
'EnumObjectName',
'EnumObjectName description',
$permissions
)
);
}
);
}
public static function createEnumType(string $name, string $description, object $enumValues): EnumTypeDefinitionNode
{
$enumDefinition = "\"$description\"\n";
$enumDefinition .= "enum $name {\n";
foreach ($enumValues as $i => $permission) {
if(!empty($permission->description)) {
$enumDefinition .= "\"{$permission->description}\"\n";
}
$enumDefinition .= $permission->ui_name . " #enum(value: \"".$permission->id."\")\n\n";
}
$enumDefinition .= "}\n";
return PartialParser::enumTypeDefinition($enumDefinition);
}
}
The directive #whereConstraints is written like this

GraphQL / Apollo partial update send full object block with null

Hey folks I just got in some kind of issue. In playground everything works fine but in the app is sending all object with null fields apart of the fields I am trying to update.
So I am trying to update first and last name of the user.
This is my mutation
export default gql`
mutation updateUser(
$_id: ID!
$email: String
$password: String
$firstName: String
$lastName: String
$phone: String
$description: String
$role: String
$avatar: String
$houseName: String
$streetName: String
$city: String
$postCode: String
$newUser: Boolean
$expoPushToken: String
$birthday: String
$countryOfResidence: String
$nationality: String
) {
updateUser(
_id: $_id
email: $email
password: $password
firstName: $firstName
lastName: $lastName
phone: $phone
description: $description
role: $role
avatar: $avatar
houseName: $houseName
streetName: $streetName
city: $city
postCode: $postCode
newUser: $newUser
expoPushToken: $expoPushToken
birthday: $birthday
countryOfResidence: $countryOfResidence
nationality: $nationality
) {
_id
}
}
`;
Here I am doing the mutation
this.props.updateUser(_id, firstName, lastName);
const mutationConfig = {
props: ({ mutate }) => ({
updateUser: (_id, firstName, lastName) =>
mutate({
variables: { _id, firstName, lastName }
})
})
};
What I get in DB
{ email: null,
password: null,
firstName: 'John',
lastName: 'Doe',
phone: null,
description: null,
role: null,
avatar: null,
houseName: null,
streetName: null,
city: null,
postCode: null,
newUser: null,
expoPushToken: null,
birthday: null,
countryOfResidence: null,
nationality: null }
If I do the same thing in playground
mutation{
updateUser(_id: "5cd2f8612a89ff61aed9f43c", firstName: "John", lastName:"Doe"){
_id
}
}
The response look like this
{ firstName: 'John', lastName: 'Doe' }
Any idea what can be?
SOLVED
I found the the only issue was from react-apollo#2.5.5 so I got back to react-apollo#2.4.1 until a new version is coming up

Laravel relationship : one foreignKey for several localKey relationship

I have to develop an api with Laravel and i'm stuck with this problem:
I need to get all matchs and for each match i need to get the users who belongs to this match (there is 2 users per match)
I've search answer for this problem and i found this https://github.com/topclaudy/compoships but it didn't worked for me.
So far, i have this :
class Match extends Model
{
protected $table = 'matchs';
public $primaryKey = 'id';
public function playerOne() {
return $this->hasMany('App\User', 'id', 'playerOne_id');
}
public function playerTwo() {
return $this->hasMany('App\User', 'id', 'playerTwo_id');
}
}
Controller Method:
public function index()
{
$matchs = Match::with('playerOne', 'playerTwo')->orderBy('created_at', 'asc')->get();
return $matchs;
//return new MatchsCollection($matchs);
}
and my database look like this :
(Matchs table)
id | playerOne_id | playertwo_id | winner_id | status
------------------------------------------------------------
1 | 1 | 3 | 0 | PENDING
2 | 2 | 1 | 0 | PENDING
3 | 3 | 2 | 0 | PENDING
and users table :
id | name | email
-----------------------------------
1 | John | John#email.com
2 | Mark | Mark#email.com
3 | Harry | Harry#email.com
and when i call my api i get this result:
[
{
id: 1,
playerOne_id: 1,
playerTwo_id: 3,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-10 00:00:00",
updated_at: null,
users: [
{
id: 1,
name: "John",
email: "John#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
}
]
},
{
id: 2,
playerOne_id: 2,
playerTwo_id: 1,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-11 08:26:28",
updated_at: "2019-01-11 08:26:28",
users: [
{
id: 2,
name: "Mark",
email: "Mark#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:40:13",
updated_at: "2019-01-11 10:40:13"
}
]
},
{
id: 3,
playerOne_id: 3,
playerTwo_id: 2,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-11 08:45:22",
updated_at: "2019-01-11 08:45:22",
users: [
{
id: 3,
name: "harry",
email: "harry#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:40:13",
updated_at: "2019-01-11 10:40:13"
}
]
}
]
What i wan't to get is this result (i just show you the first match)
[
{
id: 1,
playerOne_id: 1,
playerTwo_id: 3,
winner_id: 0,
status: "PENDING",
created_at: "2019-01-10 00:00:00",
updated_at: null,
users: [
{
id: 1,
name: "John",
email: "John#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
},
{
id: 3,
name: "Harry",
email: "Harry#email.com",
email_verified_at: null,
created_at: "2019-01-11 10:38:26",
updated_at: "2019-01-11 10:38:26"
}
]
}
]
is this possible in Laravel ? thanks :)
To be able to return all the players as part of the user array you could either change the relation between Match and User to be a belongsToMany (many-to-many) or simply manipulate the match data before returning it from the controller.
Changing it to a BelongsToMany would be my recommendation since it semantically makes more sense, however, I understand that (depending on how much work you've done so far) it may not be practical.
You would need to:
rename your matchs table to be matches (this will also mean you can remove
protected $table = 'matchs'; from your Match model class ).
Create a table called match_user. If your project is using migrations then you can run:
php artisan make:migration create_match_user_table --create=match_user
In that migration you would need to change the contents of the up() method to be:
public function up()
{
Schema::create('match_user', function (Blueprint $table) {
$table->integer('match_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->primary(['match_id', 'user_id']);
});
}
Run php artisan migrate
In your Match model change the users() relationship to be:
public function users()
{
return $this->belongsToMany(User::class)->withTimestamps();
}
If the data you have in your tables is dummy data you can skip this step. Otherwise, you'll need to move the playerOne_id and playertwo_id information to the new pivot table. You could do that will the following:
Match::all()->each(function ($match) {
$match->users()->attach([$match->playerOne_id, $match->playertwo_id]);
});
It doesn't really matter where you run this as it only has to be run once so after you've run it you can delete it. I would usually run this in a route or in tinker.
Then comes the clean up. Again, if the data you have is just dummy data, then I would just remove the playerOne_id and playertwo_id the original migration, otherwise create a new migration that removes those field Dropping columns docs
Once the above has been done you would just need to do the following in your controller to get the results:
$matches = Match::with('users')->latest()->get();

Laravel Eloquent with 'with' and 'wherehas'

Let's say I have three databases, player, credit, and photo, linked with foreign keys:
player
id | name | address
credit
id | player_id | credit_status
photo
id | player_id
Say I want to get all players who has credit_status $status, I'd do it like this:
$status = 'bar';
Player::with('photo','credit')->whereHas('credit', function ($q) use ($status) {
$q->where('credit_status', $status)->with('credit_status');
})->paginate(15);
This will lists all players who has credit_status $credit, but it still lists all credits by that player regardless of the status.
The output is something like:
{
id: 1
name: Andrew
address: home
photo: {
id: 2
photo: image1
}
credit: {
[
{
id: 6
credit_status: foo,
id: 2
credit_status: bar
}
]
}
},
{
id: 2
name: Mark
address: home
photo: {
id: 5
photo: image4
}
credit: {
[
{
id: 10
credit_status: foo,
id: 6
credit_status: bar,
id: 8
credit_status: bar
}
]
}
}
I want to filter the credit in with('credit') also.
My desired output:
{
id: 1
name: Andrew
address: home
photo: {
id: 2
photo: image1
}
credit: {
[
{
id: 2
credit_status: bar
}
]
}
},
{
id: 2
name: Mark
address: home
photo: {
id: 5
photo: image4
}
credit: {
[
{
id: 6
credit_status: bar,
id: 8
credit_status: bar
}
]
}
}
You can just do the same filtering on the with (constraining eager loading):
$creditFilter = function ($q) use ($status) {
$q->where('credit_status', $status);
};
Player::with(['photo', 'credit' => $creditFilter])
->whereHas('credit', $creditFilter)
->paginate(15);
You can save that closure and pass it to the with and whereHas so you don't have to type the same identical closure twice.
Laravel 5.6 Docs - Eloquent - Relationships - Eager Loading - Constraining Eager Loads
If you want to filter credits too then you have to also use the condition in the credit. Actually whereHas() and with() work independently they are not dependent on each other.
$status = 'bar';
Player::with(['photo','credit' => function($query) use ($status){
$query->where('credit_status', $status)->with('credit_status');
}])->whereHas('credit', function ($q) use ($status) {
$q->where('credit_status', $status)->with('credit_status');
})->paginate(15);
You only need to make conditional with the 'with' function; not with the 'whereHas' function.
$creditFilter = function ($q) use ($status) {
$q->where('credit_status', $status);
};
Player::with(['photo', 'credit'])
->with(['credit' => $creditFilter])
->paginate(15);

Resources