Update at the end
I have this API for a social media app in iOS builded in Laravel and i've been having problems with the feed section, the Eloquent code for the feed is giving me the wrong id for each posts.
public function feed(){
if (request()->has('page')) {
$pagesize = 20;
$query = Posts::join('clase_user', function ($join) {
$user = JWTAuth::parseToken()->authenticate();
$join->on('clase_user.clase_id', '=', 'posts.clase_id')
->where('clase_user.user_id', '=', $user->id);
})
->with('user')
->skip(((int)request()->get('page') - 1) * $pagesize)
->take($pagesize)->get();
return $query;
}else{
return response()->json(['error' => 'Page not specified'], 500);
}
}
the return is this:
{
"posts": [
{
"id": 3,
"user_id": 1,
"clase_id": 1,
"thumbNail": null,
"text": "Hola Chicos",
"archivo": null,
"created_at": "2017-07-20 00:00:00",
"updated_at": "2017-07-20 00:00:00",
"user": {
"id": 1,
"name": "Juan Carlos",
"about": "Hola me llamo Juan Carlos",
"handler": "Juantvz50",
"pp": null,
"verify": 1,
"email": "jc_estevezr#hotmail.com",
"deleted_at": null,
"LoginId": null,
"created_at": "2017-07-20 23:52:28",
"updated_at": "2017-07-20 23:52:28"
}
},
{
"id": 1,
"user_id": 1,
"clase_id": 2,
"thumbNail": null,
"text": "Que pex Prrrro",
"archivo": null,
"created_at": "2017-07-20 00:00:00",
"updated_at": "2017-07-20 00:00:00",
"user": {
"id": 1,
"name": "Juan Carlos",
"about": "Hola me llamo Juan Carlos",
"handler": "Juantvz50",
"pp": null,
"verify": 1,
"email": "jc_estevezr#hotmail.com",
"deleted_at": null,
"LoginId": null,
"created_at": "2017-07-20 23:52:28",
"updated_at": "2017-07-20 23:52:28"
}
},
{
"id": 3,
"user_id": 1,
"clase_id": 1,
"thumbNail": null,
"text": "Que Onda Chicos",
"archivo": null,
"created_at": "2017-07-20 00:00:00",
"updated_at": "2017-07-20 00:00:00",
"user": {
"id": 1,
"name": "Juan Carlos",
"about": "Hola me llamo Juan Carlos",
"handler": "Juantvz50",
"pp": null,
"verify": 1,
"email": "jc_estevezr#hotmail.com",
"deleted_at": null,
"LoginId": null,
"created_at": "2017-07-20 23:52:28",
"updated_at": "2017-07-20 23:52:28"
}
}
]
}
Even though my database has different ids for each one:
Database
it looks like something is wrong in some part of my code, I don't know in which part if you want to see more code just ask in the comments.
UPDATE
the id it's giving me is in fact the id of the table clase_user that is the pivot table I use to join many to many the classes with the users (like when you follow someone on youtube), I think is because i'm saying Posts::join('clase_user', any ideas to also include the the id of the post?
clase_user
Similar to this one: Join with where query in Laravel returns wrong timestamps
Because of your JOIN the attributes are getting overridden (not only the id but also the created_at/updated_at timestamps). Therefore you should define a select statement for your specific attributes.
Related
I'm fetching data to paginate but pagination is not working, the fetch response is added below, if I return the query relation it retrieves correct data but when I pass it to the custom response function it fetch data only but not the pagination links
try {
$type = 'success';
$status_code = 200;
$message = 'Posts data listed.';
$response = PostResource::collection(Post::with(['associate:id,name,avatar', 'comments:id,commenter_id,commentable_id,comment,created_at'])
->latest('posts.created_at')
->paginate(2));
} catch (Exception $e) {
$type = 'error';
$status_code = $e->getCode();
$message = $e->getMessage();
$response = false;
}
return response_data($type, $status_code, $message, $response);
Here is my response_data function code
function response_data($type, $status, $message = false, $response)
{
return response()->json(['type' => $type, 'status' => $status, 'message' => $message, 'response' => $response]);
}
but if return the query directly it retrieves data with pagination
here is my response that retrieved by the collection
{
"type": "success",
"status": 200,
"message": "Posts data listed.",
"response": [
{
"id": 32,
"associate_id": 5,
"title": "Test Title",
"content": "Post descriptuoin",
"image": "https://oppazz.oppazzgiftcode.com/images/posts/1632472717.Laravel.png",
"created_at": "2 months ago",
"associate": {
"name": "Code Logic Technologies Pvt. Ltd.",
"avatar": "https://oppazz.oppazzgiftcode.com/images/associates/1633002782_logo.png"
},
"comments": [
{
"id": 13,
"commenter_id": "62",
"commentable_id": "32",
"comment": "Nice offer",
"created_at": "2 months ago",
"user": {
"id": 62,
"name": "Rikesh Shakya",
"username": "rikesh-shakya",
"mobile_number": "9823783191",
"email": "skybks5#gmail.com",
"provider_id": null,
"avatar": "https://oppazz.oppazzgiftcode.com/images/logo.png",
"email_verified_at": null,
"status": "Y",
"created_at": "2021-06-11T18:05:07.000000Z",
"updated_at": "2021-06-11T18:05:07.000000Z",
"created_by": null,
"updated_by": null,
"device_type": null
}
},
{
"id": 16,
"commenter_id": "88",
"commentable_id": "32",
"comment": "tetetete",
"created_at": "2 months ago",
"user": {
"id": 88,
"name": "Neelam Kera",
"username": "neelam-ranjitkar",
"mobile_number": "9860322060",
"email": "shresthaneelam19#gmail.com",
"provider_id": null,
"avatar": "https://oppazz.oppazzgiftcode.com/images/logo.png",
"email_verified_at": null,
"status": "Y",
"created_at": "2021-07-15T14:08:21.000000Z",
"updated_at": "2021-07-15T14:08:21.000000Z",
"created_by": null,
"updated_by": null,
"device_type": null
}
}
],
"associate_social_sites": {
"id": 5,
"associate_id": 5,
"facebook": "https://www.fachook.com",
"instagram": "https://www.instagram.com",
"twitter": "https://www.twitter.com",
"status": "Y",
"created_at": null,
"updated_at": "2021-09-24T09:29:57.000000Z",
"created_by": null,
"updated_by": null,
"device_type": null
}
},
{
"id": 31,
"associate_id": 9,
"title": "OppazZ Coffee For Happiness (Giveaway series)",
"content": "OppazZ",
"image": "https://oppazz.oppazzgiftcode.com/images/posts/1632367205.kamloops-art-page-2.jpg",
"created_at": "2 months ago",
"associate": {
"name": "OppazZ",
"avatar": "https://oppazz.oppazzgiftcode.com/images/associates/1622551849_184399208_2242958835839866_1824735327179728878_n.jpg"
},
"comments": [],
"associate_social_sites": null
}
]
}
how can it be resolved and fetch pagination link along with the data fetched above
The Laravel paginator result classes implement the Illuminate\Contracts\Support\Jsonable interface contract and expose the toJson method, so it's very easy to convert your pagination results to JSON.
You may also convert a paginator instance to JSON by simply returning it from a route or controller action:
https://laravel.com/docs/5.3/pagination#converting-results-to-json
How do I find the exact record to fetch the price if I pass the weight and respective fields.
for eg:
if I am passing weight and selecting state, district as below,
$weight = 100
$state_id = 1
$district_id = 1
The prompt record that should be displayed is 1, I am not getting any record.
My Controller
$shipping_rate = ShippingRate::where('state_id', $state_id)->where('district_id', $district_id)->where(function ($query) use ($weight) {
$query->where('weight_from', '<=', $weight);
$query->where('weight_to', '>=', $weight);
})->first();
I noticed that the when I remove the $query->where('weight_to', '>=', $weight); results are being fetched. I assume that this could be not accurate.
So how do I find the values in between the range?
When I use
Here is my JSON:
[{
"id": 1,
"state_id": 1,
"district_id": 1,
"weight_to": "0",
"weight_from": "1450",
"price": "100",
"deleted_at": null,
"created_at": "2021-06-04T06:07:48.000000Z",
"updated_at": "2021-06-07T16:54:58.000000Z"
}, {
"id": 2,
"state_id": 1,
"district_id": 1,
"weight_to": "1450",
"weight_from": "2450",
"price": "150",
"deleted_at": null,
"created_at": "2021-06-04T06:07:48.000000Z",
"updated_at": "2021-06-04T06:07:48.000000Z"
}, {
"id": 3,
"state_id": 1,
"district_id": 1,
"weight_to": "2450",
"weight_from": "3450",
"price": "250",
"deleted_at": null,
"created_at": "2021-06-04T06:07:48.000000Z",
"updated_at": "2021-06-04T06:07:48.000000Z"
}, {
"id": 4,
"state_id": 1,
"district_id": 5,
"weight_to": "100",
"weight_from": "1450",
"price": "50",
"deleted_at": null,
"created_at": "2021-06-04T06:07:48.000000Z",
"updated_at": "2021-06-04T06:07:48.000000Z"
}, {
"id": 5,
"state_id": 1,
"district_id": 6,
"weight_to": "100",
"weight_from": "1450",
"price": "50",
"deleted_at": null,
"created_at": "2021-06-04T06:07:48.000000Z",
"updated_at": "2021-06-04T06:07:48.000000Z"
}]
table structure
Hi you have a small issue, not syntax issue it's a logical issue.
Just replace this
->where(function ($query) use ($weight) {
$query->orWhere('weight_from', '<=', $weight);
$query->orWhere('weight_to', '>=', $weight);
})
With this
->where(function ($query) use ($weight) {
$query->Where('weight_from', '>=', $weight);
$query->Where('weight_to', '<=', $weight);
})
Here your weight_form is greater that weight_to, so it will work.
OR
You can try this
ShippingRate::where('state_id', $state_id)
->where('district_id', $district_id)
-->whereRaw('"'.$weight.'" between `weight_to` and `weight_from`')
->first();
I'm a laravel beginner and want to build an API with laravel 8.
I have a nested comment and replies system for my posts
and the relation between comments and posts are polymorphic
i want to show a post and comments and replies as JSON
but i don't know my query is correct or not , because i can't understand relation between comments and replies and i just can see all comments and replies related to the post.
this is the JSON response in postman :
[
{
"id": 45,
"category_id": 11,
"user_id": 1,
"title": "title example",
"body": "body example",
"video": null,
"study_time": "8",
"likes": null,
"status": null,
"tags": [],
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:09:14.000000Z",
"updated_at": "2021-05-01T14:09:14.000000Z",
"comments": [
{
"id": 13,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": null,
"name": "sara",
"email": "sara#gmail",
"comment": "its good",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:10:14.000000Z",
"updated_at": "2021-05-01T14:10:14.000000Z"
},
{
"id": 14,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 13,
"name": "john",
"email": "john#gmail.com",
"comment": "its not good",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:10:50.000000Z",
"updated_at": "2021-05-01T14:10:50.000000Z"
},
{
"id": 15,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 14,
"name": "sara",
"email": "sara#gmail.com",
"comment": "why?",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:11:12.000000Z",
"updated_at": "2021-05-01T14:11:12.000000Z"
}
],
}
]
as you see , the replies aren't in array and it's not clear which is a replay and which is a comment
so this is my PostDetailsController :
class PostDetailsController extends Controller
{
/**
* Disply post and comments with replies
**/
public function showPost($id){
$postFind = Post::find($id);
if(is_null($postFind)){
return response()->json('not found' , 404);
}
$post = Post::with('comments')->find($id);
return response()->json([$post] , 200);
}
about comments :
comments table :
chema::create('comments', function (Blueprint $table) {
$table->id();
$table->integer('commentable_id');
$table->string('commentable_type');
$table->unsignedBigInteger('parent_id')->nullable(); //nullable for comments , with value for replies
$table->string('name' , 45);
$table->string('email');
$table->longText('comment');
$table->foreign('parent_id')->references('id')->on('comments')->onUpdate('cascade')->onDelete('cascade');
$table->rememberToken();
$table->softDeletes();
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP'));
});
}
so if parent_id is NULL it's a comment and if has values ( id of parent comments ) it's a reply. (and reply of reply has previous reply id as parent_id )
and the models :
Post :
class Post extends Model
{
protected $fillable = [
'user_id' ,
'category_id' ,
'title' ,
'body' ,
'study_time',
'status',
'tags',
];
public function comments()
{
return $this->morphMany(Comment::class, 'commentable' );
}
}
Comment :
class Comment extends Model
{
protected $fillable = [
'parent_id' , 'name' , 'email' , 'comment' , 'status' , 'images'
];
use HasFactory;
public function commentable(){
return $this->morphTo();
}
public function replies()
{
return $this->hasMany(Comment::class, 'parent_id');
}
}
so can you please tell me is my query wrong?
and what should i do for show replies in an array ?
EDIT
i changed my code to this :
class PostDetailsController extends Controller
{
/**
* Disply post and comments with replies
**/
public function showPost($id){
$postFind = Post::find($id);
if(is_null($postFind)){
return response()->json('not found' , 404);
}
$post = Post::with('comments.replies')->find($id);
return response()->json([$post] , 200);
}
and the response is :
[
{
"id": 45,
"category_id": 11,
"user_id": 1,
"title": "title example",
"body": "body example",
"video": null,
"study_time": "8",
"likes": null,
"status": null,
"tags": [],
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:09:14.000000Z",
"updated_at": "2021-05-01T14:09:14.000000Z",
"comments": [
{
"id": 13,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": null,
"name": "sara",
"email": "sara#gmail",
"comment": "its good",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:10:14.000000Z",
"updated_at": "2021-05-01T14:10:14.000000Z",
"replies": [
{
"id": 14,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 13,
"name": "john",
"email": "john#gmail.com",
"comment": "its not good",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:10:50.000000Z",
"updated_at": "2021-05-01T14:10:50.000000Z"
}
]
},
{
"id": 14,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 13,
"name": "john",
"email": "john#gmail.com",
"comment": "its not good",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:10:50.000000Z",
"updated_at": "2021-05-01T14:10:50.000000Z",
"replies": [
{
"id": 15,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 14,
"name": "sara",
"email": "sara#gmail.com",
"comment": "why?",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:11:12.000000Z",
"updated_at": "2021-05-01T14:11:12.000000Z"
}
]
},
{
"id": 15,
"commentable_id": 45,
"commentable_type": "App\\Models\\Post",
"parent_id": 14,
"name": "sara",
"email": "sara#gmail.com",
"comment": "why?",
"images": null,
"like": null,
"dislike": null,
"status": null,
"remember_token": null,
"deleted_at": null,
"created_at": "2021-05-01T14:11:12.000000Z",
"updated_at": "2021-05-01T14:11:12.000000Z",
"replies": []
}
],
}
]
so i can see the replies of every comment or reply in the response , but as you see the replies are repeated and the last reply that has no reply ,has "replies[]"
What should I do to not show the replay again?
So a couple things here that you could do, firstly your replies have the commentable_type and commentable_id whereas these should probably be nullable since when looking at replies you only really need to know its parent and not the post as you can get this through the parent.
This then allows your $post->comments relationship to not return replies to comments.
Next you can use dot notation to add replies to your comments.
$post = Post::with('comments.replies')->find($id);
This would then return something like this
[
{
"id": 1,
"title": "Title",
"body": "Body",
"created_at": "2021-05-01T11:34:27.000000Z",
"updated_at": "2021-05-01T11:34:28.000000Z",
"comments": [
{
"id": 1,
"post_id": 1,
"parent_id": null,
"body": "No Replies",
"created_at": "2021-05-01T11:34:41.000000Z",
"updated_at": "2021-05-01T11:34:43.000000Z",
"replies": []
},
{
"id": 2,
"post_id": 1,
"parent_id": null,
"body": "One Reply",
"created_at": "2021-05-01T11:35:18.000000Z",
"updated_at": "2021-05-01T11:35:19.000000Z",
"replies": [
{
"id": 3,
"post_id": null,
"parent_id": 2,
"body": "Reply",
"created_at": "2021-05-01T11:35:29.000000Z",
"updated_at": "2021-05-01T11:35:32.000000Z"
}
]
}
]
}
]
I am using sequelize(4.42.0) along with express. I have two tables categories and main_categories which has the following association:
db.main_categories.hasMany(db.categories);
db.categories.belongsTo(db.main_categories);
I tried to get list of all the categories grouped under their main category using following sequelize command:
var _categoryList = await models.main_categories.findAll({include: [{model: models.categories}], group:['main_categories.id']});
res.send({category_list: _categoryList});
The output of the above sequelize code is :
{
"category_list": [
{
"id": 1,
"name": "Physics",
"created_at": null,
"updated_at": null,
"categories": [
{
"id": 1,
"name": "Mechanics",
"created_at": "2019-03-21T03:39:48.000Z",
"updated_at": "2019-03-21T03:39:48.000Z",
"main_category_id": 1
}
]
},
{
"id": 2,
"name": "Chemistry",
"created_at": null,
"updated_at": null,
"categories": [
{
"id": 6,
"name": "General and Physical Chemistry",
"created_at": "2019-03-21T03:42:54.000Z",
"updated_at": "2019-03-21T03:42:54.000Z",
"main_category_id": 2
}
]
},
{
"id": 3,
"name": "Zoology",
"created_at": null,
"updated_at": null,
"categories": []
},
{
"id": 4,
"name": "Botany",
"created_at": null,
"updated_at": null,
"categories": []
}
]
}
The expected output for me was that the categories array inside each main_category would contain details of all the categories falling under the same main category instead of just one as shown in the output above.
My main_categories table with data:
my categories table with data:
So I later found out that as the associations had already been declared we just need to include the model without any groupby clause. So the final code that worked for me was :
var _categoryList = await models.main_categories.findAll({include: [{model: models.categories}]});
i have the following Template model and TestCategory hasMany Test:
class Template extends Model
{
protected $table = 'test_templates';
protected $fillable = [
'customers_id',
'tests_categories_id',
'tests_id'
];
public function customers(){
return $this->belongsTo(Customer::class, 'customers_id');
}
public function testCategories(){
return $this->belongsTo(TestCategory::class, 'tests_categories_id');
}
public function tests(){
return $this->belongsTo(Test::class, 'tests_id');
}
}
And I have the following index method defined in TemplateController
public function index($customerId)
{
$templates = Template::with( 'customers','testCategories','tests')
->where('customers_id', $customerId)->get();
$templates = $templates->groupBy('tests_categories_id');
$templates = json_decode($templates, true);
$templates = array_values($templates);
return response()->json(['Status' => True, 'template' => $templates]);
}
I got the following json output:
{
"Status": true,
"template": [
[
{
"id": 1,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 1,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "9842********",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 1,
"tests_category_id": 1,
"name": "Lamination",
"created_at": null,
"updated_at": null
}
},
{
"id": 2,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 2,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984*******",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 2,
"tests_category_id": 1,
"name": "Paper Type",
"created_at": null,
"updated_at": null
}
}
],
[
{
"id": 7,
"customers_id": 1,
"tests_categories_id": 2,
"tests_id": 8,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984******",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 2,
"name": "Scratch Test",
"created_at": null,
"updated_at": null
},
"tests": {
"id": 8,
"tests_category_id": 2,
"name": "HRN Visibility",
"created_at": null,
"updated_at": null
}
}
]
]
}
I want to collect all the tests as per the test_categories without reprinting customers and test_categories as following:
{
"Status": true,
"template": [
[
{
"id": 1,
"customers_id": 1,
"tests_categories_id": 1,
"tests_id": 1,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984*****",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 1,
"name": "General Tests",
"created_at": null,
"updated_at": null
},
"tests": {
{
"id": 1,
"tests_category_id": 1,
"name": "Lamination",
"created_at": null,
"updated_at": null
},
{
"id": 2,
"tests_category_id": 1,
"name": "Paper Type",
"created_at": null,
"updated_at": null
}
}
}
],
[
{
"id": 7,
"customers_id": 1,
"tests_categories_id": 2,
"tests_id": 8,
"created_at": "2018-09-10 11:03:44",
"updated_at": "2018-09-10 11:03:44",
"customers": {
"id": 1,
"name": "Queen PVT.Ltd",
"address": "gotham",
"contact_number": "984265*****",
"created_at": "2018-09-10 11:03:43",
"updated_at": "2018-09-10 11:03:43"
},
"test_categories": {
"id": 2,
"name": "Scratch Test",
"created_at": null,
"updated_at": null
},
"tests": {
{
"id": 8,
"tests_category_id": 2,
"name": "HRN Visibility",
"created_at": null,
"updated_at": null
}
}
}
]
]
}
If I understand the problem correctly the tests are being separated into different templates when you are expecting some of them to be related to the same template.
This is because your Template class is storing the test_id that it belongs to and it can only store 1 test per Template. So when you are calling Template::with('tests') it can only return one Test::class.
Because you expect to see that many Tests belongTo one Template, and when you look at the relationship from the Template to Test, you expect that a Template has many Tests. Therefore, your Template class should implement the hasMany relationship like so:
class Template extends Model
{
protected $fillable = [
'customers_id',
'tests_categories_id' // notice the removal of the test_id which should also be removed from the test_templates migration
];
...
public function tests(){
return $this->hasMany(Test::class); //change belongsTo to hasMany
}
...
}
Instead of storing the test_id on the Template class you should be storing the template_id on your Test class and look something like so:
class Test extends Model
{
protected $fillable = [
...
'test_template_id' // This should be added to $fillable and the tests migration
...
];
...
public function template(){
return $this->belongsTo(Template::class, test_template_id);
}
...
}
Now when you call Template::with('tests') it can return many Test::class and should match your expected output.