I need to add multiple values through checkboxes to the company_user pivot table
This is what i have tried so far
Livewire Component
<?php
namespace App\Http\Livewire;
use App\Models\User;
use App\Models\Company;
use Livewire\Component;
use Livewire\WithPagination;
class UserAssignCompanySection extends Component
{
use WithPagination;
public $sortBy = 'id';
public $sortAsc = false;
public $user;
public $companies;
public $search;
public $confirmingCompanyRemoval = false;
public $selected = [];
protected $rules = [
'user.name' => 'required',
'user.email' => 'required',
'user.title' => 'required',
'user.first_name' => 'required',
'user.last_name' => 'nullable',
'user.mobile' => 'nullable',
'user.phone' => 'nullable',
'user.is_customer' => 'boolean',
'user.is_provider' => 'boolean',
];
// Table Sort
public function sortBy($field)
{
if($field == $this->sortBy){
$this->sortAsc = !$this->sortAsc;
}
$this->sortBy = $field;
}
public function updatedSelected()
{
$this->user->companies()->sync($this->selected);
}
public function mount(User $user)
{
$this->user = $user;
$this->companies = Company::all();
$this->selected = $this->user->companies->pluck('id')->toArray();
}
public function render()
{
return view('livewire.user-assign-company-section');
}
}
<div>
<div class="grid grid-cols-1 text-left xl:grid-cols-6">
<div class="col-span-3 p-4">
<p class="text-lg underline">Name</p>
<x-jet-input wire:model.defer="user.name" id="name" type="text" disabled class="block w-full mt-1 bg-gray-100" />
<x-jet-input-error for="user.name" class="mt-2" />
<br>
<p class="text-lg underline">Assigned Company List</p>
#json($selected);
#foreach($companies as $company)
<div class="mt-1">
<input type="checkbox" value="{{ $company->id }}" wire:model="selected">
{{ $company->name }} <br>
</div>
#endforeach
<br>
<x-jet-button>Back</x-jet-button>
<br>
</div>
</div>
</div>
Syncing records to the pivot table doesn't work properly
#json($selected)
Gives the proper results when the page gets loaded.
When i add a new chckbox, it includes the id of the company as a string "1"
#json($selected) shows
[2,3,4,"1"]
At this moment the new pivot record is been added
Then when i select the back button
#json($selected) shows something like this
[2,3,4,1]
The back button doesn't take me back(I will be placed on the same page)
Then when it comes to Unchecking. It doesn't work at all
#json($selected) doesn't show sync with unchecked values
Edit :
Company Model
<?php
namespace App\Models;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Company extends Model
{
use SoftDeletes;
use HasFactory;
protected $fillable = [
'name',
'phone',
'vat_no',
'address',
'is_customer',
'is_provider',
'is_self',
];
public function jobs()
{
return $this->hasMany(Job::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
}
User Model
<?php
namespace App\Models;
use App\Models\Company;
use Laravel\Jetstream\HasProfilePhoto;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use SoftDeletes;
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
use Historyable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
'title',
'first_name',
'last_name',
'mobile',
'phone',
'is_customer',
'is_provider',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
'profile_photo_url',
];
public function companies()
{
return $this->belongsToMany(Company::class);
}
}
I did this
public function updatedSelected()
{
dd($this->user, $this->selected);
$this->user->companies()->sync($this->selected);
}
and it gives
App\Models\User {#1683 ▼
#fillable: array:10 [▼
0 => "name"
1 => "email"
2 => "password"
3 => "title"
4 => "first_name"
5 => "last_name"
6 => "mobile"
7 => "phone"
8 => "is_customer"
9 => "is_provider"
]
#hidden: array:4 [▼
0 => "password"
1 => "remember_token"
2 => "two_factor_recovery_codes"
3 => "two_factor_secret"
]
#casts: array:2 [▼
"email_verified_at" => "datetime"
"deleted_at" => "datetime"
]
#appends: array:1 [▼
0 => "profile_photo_url"
]
#connection: "mysql"
#table: "users"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:26 [▶]
#original: array:26 [▶]
#changes: []
#classCastCache: []
#dates: []
#dateFormat: null
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"companies" => Illuminate\Database\Eloquent\Collection {#1688 ▼
#items: []
}
]
#touches: []
+timestamps: true
#visible: []
#guarded: array:1 [▶]
#rememberTokenName: "remember_token"
#forceDeleting: false
#accessToken: null
}
array:1 [▼
0 => "1"
]
You could add a function in livewire component, whenever the user click on checkbox you add the value to $selected attribute.
In livewire component(this add/remove company id):
public function addCompany($companyId) {
if (!in_array($companyId, $this->selected)) {
$this->selected [] = $companyId;
}else{
$this->selected = array_diff($this->selected, [$companyId]);
}
}
And in your view add this your input:
wire:click="addStyle({{ $collection['id'] }})"
Related
I have a working Posts, Categories and Tags relationship. (Posts BelongsTo Categories and BelongsToMany Tags.) They work just fine at my view and index actions with no issue.
Now, for a simple "search" functionality I'm working with the Query builder. I managed to make it successfully search Posts related to my query, as long as the terms are compared to fields from Posts and Categories, but I would also want to make it work with Tags.
This is my (working) Controller:
public function search()
{
$search = $this->request->getQuery('query');
$posts = $this->Posts->find('all');
$posts->contain(['Categories','Tags']);
if(!empty($search)) {
$posts->where(['or' => [
['Posts.title LIKE' => '%'.$search.'%', 'Posts.status' => 'published'],
['Posts.content LIKE' => '%'.$search.'%', 'Posts.status' => 'published'],
['Categories.name LIKE' => '%'.$search.'%','Posts.status' => 'published'],
]]);
} else {
$posts->where(['Posts.status' => 'published']);
};
$posts = $this->paginate($posts);
$this->set(compact('posts'));
}
These are my (working) Models:
// Posts Table
class PostsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('posts');
$this->setDisplayField('title');
$this->setPrimaryKey('id');
$this->belongsTo('Categories', [
'foreignKey' => 'category_id',
'joinType' => 'INNER',
]);
$this->belongsToMany('Tags',[
'foreignKey' => 'post_id',
'targetForeignKey' => 'tag_id',
'joinTable' => 'posts_tags'
]);
}
}
// Categories Table
class CategoriesTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('categories');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->hasMany('Posts', [
'foreignKey' => 'category_id',
]);
}
}
// Tags Table
class TagsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('tags');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->belongsToMany('Posts', [
'foreignKey' => 'tag_id',
'targetForeignKey' => 'post_id',
'joinTable' => 'posts_tags',
]);
}
}
// PostsTags Table
class PostsTagsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('posts_tags');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Posts', [
'foreignKey' => 'post_id',
'joinType' => 'INNER',
]);
$this->belongsTo('Tags', [
'foreignKey' => 'tag_id',
'joinType' => 'INNER',
]);
}
}
And this is my view:
<?php $search = $this->request->getQuery('query'); ?>
<div class="posts index content">
<h1>Search Posts</h1>
<?= $this->Form->create(NULL,['type' => 'get']) ?>
<?= $this->Form->control('query',['default' => $search]) ?>
<?= $this->Form->button('submit') ?>
<?= $this->Form->end() ?>
<?php foreach ($posts as $post): ?>
<div class="card">
<!-- Here goes the Post data -->
</div>
<?php endforeach; ?>
</div>
<div class="paginator">
<ul class="pagination">
<?= $this->Paginator->first('<< ' . __('first')) ?>
<?= $this->Paginator->prev('< ' . __('previous')) ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next(__('next') . ' >') ?>
<?= $this->Paginator->last(__('last') . ' >>') ?>
</ul>
<p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}')) ?></p>
</div>
So when I submit the form, it filters my posts according to those conditions. But when I try adding a field from my Tags model to the search query, it breaks.
I tried adding the line:
['Tags.name LIKE' => '%'.$search.'%', 'Posts.status' => 'published']
...under:
['Categories.name LIKE' => '%'.$search.'%','Posts.status' => 'published']
But then when I introduce a query term, it throws me a "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Tags.name' in 'where clause'" error.
Same thing happens if instead of "$posts->where(...)" I use the "$posts->find('all',['conditions' => [...]]):" option.
So I'm stumped... How can I search a term within a HABTM relationship?
What am I missing?
User ndm's comment did the trick. So in case it wasn't clear enough (and if someone ever finds the same issue I did in the future), here's my final working controller:
public function search()
{
$search = $this->request->getQuery('query');
$posts = $this->Posts->find('all')
->leftJoinWith('Tags')
->group(['Posts.id']);
$posts->contain(['Categories','Tags']);
if(!empty($search)) {
$posts->where(['or' => [
['Posts.title LIKE' => '%'.$search.'%', 'Posts.status' => 'published'],
['Posts.content LIKE' => '%'.$search.'%', 'Posts.status' => 'published'],
['Categories.name LIKE' => '%'.$search.'%','Posts.status' => 'published'],
['Tags.name LIKE' => '%'.$search.'%','Posts.status' => 'published']
]]);
} else {
$posts->where(['Posts.status' => 'published']);
};
$posts = $this->paginate($posts);
$this->set(compact('posts'));
}
I'm trying to display the users name using blade, but Its says null.
This is the controller
public function edit(Project $project)
{
//
$project = Project::findOrFail($project->id);
$assignees = User::with('roles')->get();
return view('projects.edit',['project'=>$project,'assignees'=>$assignees]);
}
When I dd($assignees) all the columns appears including the name column.
#attributes: array:10 [▼
"id" => 4
"name" => "Kitten Moose"
"email" => "kitten#karata.com"
"email_verified_at" => null
"password" => "$2y$10$iG4EZBq13ExGOVIrGNhEQeVvordWc3ibWfYgdaq6x0wqefetqXixe"
"remember_token" => null
"created_at" => "2020-07-03 05:11:56"
"updated_at" => "2020-07-11 09:37:49"
"team_id" => 3
"deleted_at" => null
]
On blade:
<div class="form-group">
<label for="exampleFormControlSelect1">Assignee</label>
<select name="assignee_id" class="js-example-basic-single">
<option value=" ">Select</option>
#foreach($assignees as $assignee)
<option value="{{$assignee->id}}">{{$assignee->name}}</option>
#endforeach
</select>
</div>
Using the blade above, I get {"id":1,"name":null} everything else displays correctly
I'm not sure if this could be caused by the model, but I have this in the user model:
public function assignee()
{
return $this->belongsTo(User::class);
}
If i use the variable tests everything works:
My Controller:
public function index()
{
$row = Test::count();
$tests = Test::latest()->paginate($row);
return view('tests.index',compact('tests'));
}
public function show(Test $test)
{
return view('tests.show', compact('test'));
}
My Index-View:
#foreach($tests as $test)
<div class="event">
<h4 class="eventtitle text-j4y-dark">{{ $test->body }}</h4>
Mehr lesen »
</div>
#endforeach
My Show-View:
<h4 class="eventtitle text-j4y-dark">{{ $test->body }}</h4>
but if i use the variable event the index page wokrs so it shows me all of my events but if i click on one event for more details it shows me nothing and nothing works but i don't know why? its the same code only different variables ?
I don't know what to do
My Controller:
public function index()
{
$row = Event::count();
$events = Event::latest()->paginate($row);
return view('tests.index',compact('events'));
}
public function show(Event $event)
{
return view('tests.show', compact('event'));
}
My Index-View:
#foreach ($events as $event)
<div class="event">
<h4 class="eventtitle text-j4y-dark">{{ $event->eventtitel }}</h4>
<div class="date"><i class="far fa-calendar-alt"></i>{{ date("d.m.Y", strtotime($event->datum)) }}</div>
<p class="eventtext" id="eventtext">{{ $event->eventbeschreibung }}</p>
Mehr lesen »
</div>
#endforeach
My Show-View:
<div class="event">
<h4 class="eventtitle">{{ $event->eventtite l}}</h4>
<div class="date"><i class="far fa-calendar-alt"></i>{{ date("d.m.Y", strtotime($event->datum)) }}</div>
<p class="eventtext" id="eventtext">{{ $event->eventbeschreibung }}</p>
</div>
if i use dd($test) it shows me this:
Test {#692 ▼
#fillable: array:1 [▶]
#connection: "mysql"
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:4 [▶]
#original: array:4 [▶]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
if i use dd($event) it shows me this:
Event {#660 ▼
#fillable: array:5 [▶]
+timestamps: false
#connection: null
#table: null
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: false
+wasRecentlyCreated: false
#attributes: []
#original: []
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
#hidden: []
#visible: []
#guarded: array:1 [▶]
}
if i use echo $event in my controller
it shows me this: []
if i use print_r($event)
it shows me this:
App\Event Object ( [fillable:protected] => Array ( [0] => firmenname [1] => eventtitel [2] => eventbeschreibung [3] => datum [4] => anhang ) [timestamps] => [connection:protected] => [table:protected] => [primaryKey:protected] => id [keyType:protected] => int [incrementing] => 1 [with:protected] => Array ( ) [withCount:protected] => Array ( ) [perPage:protected] => 15 [exists] => [wasRecentlyCreated] => [attributes:protected] => Array ( ) [original:protected] => Array ( ) [changes:protected] => Array ( ) [casts:protected] => Array ( ) [dates:protected] => Array ( ) [dateFormat:protected] => [appends:protected] => Array ( ) [dispatchesEvents:protected] => Array ( ) [observables:protected] => Array ( ) [relations:protected] => Array ( ) [touches:protected] => Array ( ) [hidden:protected] => Array ( ) [visible:protected] => Array ( ) [guarded:protected] => Array ( [0] => * ) )
Well, there are lot of issue with you code. First thing is the pagination.
$row = Event::count(); //This is basically counting all the events you have
$events = Event::latest()->paginate($row); // And here u telling to pageinit with all events in single page.
I personally do not think it is the right way. If I had to do I would do this way
$events = Event::orderBy('created_at','desc')->get(); //This will allthe even in single page with the latest one.
If you only need all the events and sorting is not required I would do like this
$events = Event::all(); //This is give all the events
Coming back to your question.
A variable defined inside a controller method stays inside that method and the view that method is rendering. It does not pass another blade until and unless you are declaring it globally in the provider.
For your case, i would do something like this
Route
Route::get('events',['as'=>'events','uses'=>'ControllerName#index']);
Route::get('event/{id}',['as'=>'event.detail','uses'=>'ControllerName#show']);
Controller
public function index()
{
$data['events'] = Event::orderBy('created_at','desc')->get();
return view('tests.index',$data);
}
public function show($id)
{
$data['eventDetail'] = Event::where('id',$id)->first();
return view('tests.show',$data);
}
Index view
#foreach ($events as $event)
<div class="event">
<h4 class="eventtitle text-j4y-dark">{{ $event->eventtitel }}</h4>
<<CODE HERE MORE CODE >>
Mehr lesen »
</div>
#endforeach
Event detail view
<div class="event">
<h4 class="eventtitle">{{ $eventDetail->eventtite l}}</h4>
<div class="date"><i class="far fa-calendar-alt"></i>{{ date("d.m.Y", strtotime($eventDetail->datum)) }}</div>
<p class="eventtext" id="eventtext">{{ $eventDetail->eventbeschreibung }}</p>
</div>
Hope this helps
I'm trying to use old() in Laravel but it doesn't work.
In my controller:
dd(back()->withInput());
this is the session result
#session: Store {#364 ▼
#id: "dyNmtFJVQCQmcCob4SPYEBzWHJ6TJeM9X0mzWWu7"
#name: "laravel_session"
#attributes: array:6 [▼
"_token" => "NVJATvxuHRlGbAC1jUEIjS6gbLQQEWPIIV41fLYd"
"url" => []
"_previous" => array:1 [▼
"url" => "http://localhost:8000/units/23/rents/create"
]
"_flash" => array:2 [▼
"old" => []
"new" => array:1 [▼
0 => "_old_input"
]
]
"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d" => 1
"_old_input" => array:5 [▼
"_token" => "NVJATvxuHRlGbAC1jUEIjS6gbLQQEWPIIV41fLYd"
"contract_id" => "20"
"unit_id" => "23"
"amount_paid" => "1"
"submit" => "submit"
]
]
#handler: FileSessionHandler {#363 ▶}
#started: true
value="{{ old('amount_paid')}}" I would get empty field
value="{{ old('amount_paid','test') }}" I would get 'test' in the field
I'm using Laravel 5.6
EDIT
to clarify the above code it to debug
this is my original code
controller
return back()->withInput();
view
<div class="form-group">
<label> amount paid </label>
<input type="float" class="form-control" name="amount_paid" value="{{ old('amount_paid')}}" required>
</div>
adding the method that handle the request
public function store(Request $request)
{
// updating the amount paid
$rent = new Rent;
$rent->amount_paid = $request->amount_paid;
try {
$rent->save();
} catch (\Illuminate\Database\QueryException $e) {
//return to the form and populate it
return back()->withInput();
}
return redirect('units/'.$request->unit_id);
** update **
It appear that session->flash is not working.
I tried a new installation of laravel and it is working fine
The problem was related to session it self.
I have 2
\Illuminate\Session\Middleware\StartSession::class,
one in middleware and another in $middlewareGroups[web].
this was a result of following a tutorial in youtube months ago to implement localisation :(
Hi im trying to pass all the products in the same category to a view
my products table has cat_id which related to cat_id in the categories table,
i got all the data from a product id to a single product view and i want to show related products to that product too
here my passing to product page's controller
public function product($id){
$product = Products::where('pro_id',$id)->Paginate(1);
$products=Products::where('cat_id',$product->cat_id)->get();
return view('front.product.product', compact('product','products'));
}
the $product variable works
but not the $products variable
i get the following error when try to uses $products variable
Undefined property: Illuminate\Pagination\LengthAwarePaginator::$cat_id
and my variables when trying to pass to view
#foreach($product as $show)
<div class="col-md-9">
<div class="col-md-5 grid">
<div class="thumb-image"> <img src="{!! url('public/backend/images/products/'.$show->pro_img) !!}" data-imagezoom="true" class="img-responsive" style="width: 305px; height: 400px;"> </div>
</div>
<div class="col-md-7 single-top-in">
<div class="single-para simpleCart_shelfItem">
<h1>{{$show->pro_title}}</h1>
<p>{{$show->pro_descript}}</p>
<label class="add-to item_price">{{$show->pro_price}}</label>
<div class="available">
<h6>Description</h6>
<h4>{{$show->pro_detail}}</h4>
</div>
Add to cart
</div>
</div>
<div class="clearfix"> </div>
<div class="content-top1">
<div class="clearfix"> </div>
</div>
#foreach($products as $related)
<div class="col-md-4 col-md3">
<div class="col-md1 simpleCart_shelfItem">
<a href="single.html">
<img class="img-responsive" src="{!! url('public/backend/images/products/'.$related->pro_img) !!}" alt="" />
</a>
<h3>{{$related->pro_detail}}</h3>
<div class="price">
<h5 class="item_price">{{$related->pro_price}}</h5>
Add To Cart
<div class="clearfix"> </div>
</div>
</div>
</div>
#endforeach
</div>
#endforeach
when i dd($product) with paginate(1)
LengthAwarePaginator {#237 ▼
#total: 1
#lastPage: 1
#items: Collection {#226 ▼
#items: array:1 [▼
0 => Products {#232 ▼
#table: "tbl_products"
#primaryKey: "pro_id"
+timestamp: false
#filltable: array:9 [▼
0 => "cat_id"
1 => "pro_title"
2 => "pro_descript"
3 => "pro_detail"
4 => "pro_img"
5 => "quantity"
6 => "pro_date"
7 => "pro_price"
8 => "pro_status"
]
#connection: "mysql"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:12 [▼
"pro_id" => 2
"cat_id" => 9
"pro_title" => "veston "
"pro_descript" => "look good"
"pro_detail" => "blue jean"
"pro_img" => "1516593265.jpg"
"quantity" => 8
"pro_date" => "2018-01-22 10:54:25"
"pro_price" => 400.0
"pro_status" => 0
"created_at" => "2018-01-22 03:54:25"
"updated_at" => "2018-01-22 03:54:25"
]
#original: array:12 [▼
"pro_id" => 2
"cat_id" => 9
"pro_title" => "veston "
"pro_descript" => "look good"
"pro_detail" => "blue jean"
"pro_img" => "1516593265.jpg"
"quantity" => 8
"pro_date" => "2018-01-22 10:54:25"
"pro_price" => 400.0
"pro_status" => 0
"created_at" => "2018-01-22 03:54:25"
"updated_at" => "2018-01-22 03:54:25"
]
#changes: []
#casts: []
#dates: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [▼
0 => "*"
]
}
]
}
#perPage: 1
#currentPage: 1
#path: "http://localhost:81/shop/product/2"
#query: []
#fragment: null
#pageName: "page"
}
$product = Products::where('pro_id',$id)->Paginate(1);
this code returns array
cahnge code to like below:
$product = Products::where('pro_id',$id)->first();
Here you are calling a paginator:
$product = Products::where('pro_id',$id)->Paginate(1);
Try this instead:
$product = Products::where('pro_id',$id)->first();
-or-
$product = Products::where('pro_id',$id)->paginate(1)[0];
-or finally-
$product = Product::findOrFail($id);
The latter would only work if you've set protected $primaryKey = 'pro_id'; within your class.
The details:
When you called $product = Products::where('pro_id',$id)->Paginate(1);' you are getting aPaginatorinstance back from the data layer. ThePaginatordoesn't have the properties that aProduct` does in your example, which is where the error is coming from.
A paginator is a different type of collection, but it is a type of collection, also. Think about it like an array or like a Java style ArrayList which is a complex wrapper class for an array that provides additional funcitonality. A paginator is similar, but with an extra level of abstraction to provide view based pagination. You can access objects on the 'page' within the paginator similarly to how you can get array-like access to objects in a collection.
If you just return a collection, then doing
$product = Products::where('pro_id',$id)->get();
Gives you the the matching id as a collection. In order to actually see the object you want you would need to do this:
$product = Products::where('pro_id',$id)->paginate(1)[0];
to get the first item in that collection.