I'm currently following an online course on Laravel and I'm stuck at some relations. I've been looking for a bug last 3 hours and I'm unable to find it.
Basically, I have a form which user fills in, and a field where he loads a picture. For that picture I have two separate tables in database (photos - contains info about that photo & users - where photo_id should go). I followed all the steps in the course, but upon inserting picture in my database, relation doesn't work properly. I'll provide all the code I have down below.
User-Photo relation:
public function photo(){
return $this->belongsTo('App\Photo');
}
Saving form data in database and creating a picture in controller:
public function store(UsersRequest $request)
{
$input = $request->all();
if ($file = $request->file('photo_id')){
$name = time().$file->getClientOriginalName();
$file->move('images', $name);
$photo = Photo::create(['file'=>$name]);
$input['photo_id'] = $photo->id;
}
$input['password'] = bcrypt($request->password);
User::create($input);
return redirect('/admin/users');
}
This is my form input field for a picture:
<div class="form-group">
{!! Form::label('photo_id','Photo: ') !!}
{!! Form::file('photo_id', null , ['class'=>'form-control']) !!}
</div>
This code gives me no error whatsoever, but my relations don't work properly. Photos table doesn't get filled with any data, and in my Users table, column photo_id gets filled with a photo name, not an id as it should.
I'd really appreciate any help here. If I forgot to provide anything else here, please let me know.
Like Chung Nguyễn Trần I assume that the file upload isn't working properly.
Common mistake here is that the form was not opened with 'files' => true.
See also in the docs: https://laravelcollective.com/docs/5.2/html#file-input
So you should do:
echo Form::open(['url' => 'foo/bar', 'files' => true]);
From what you say, the Photo model should contain:
public function user()
{
return $this->belongsTo(User::class)
}
The User model should contain:
public function photo()
{
return $this->hasOne(Photo::class);
// Or maybe this should be photos() and
//return $this->hasMany(Photo::class);
}
A common gotcha with relationships is making sure the id fields are correct, eg: photos table has a user_id field
And when getting to grips with how relationships work, I'd recommended working with 'artisan tinker' so you can check these simply in isolation to the rest of the code.
Related
I have a problem with updating tables that belongTo another table.
I have a users table and a recipes table. The Recipe model belongsTo the User model and the User model hasMany Recipe.
Each recipe is shown in my index view as a small card and on that card, as well as on each individual show page, I am printing recipe->author. When a recipe is created, it takes the username attribute from the users table and sets this as the author attribute on the recipes table. However, when I update the username of a user, the author attribute in the recipes table does not update accordingly.
User Model
public function recipes(){
return $this->hasMany('App\Recipe');
}
Recipe Model
public function user(){
return $this->belongsTo('App\User');
}
Can I possible add some logic in my UserController to account for this when I update a user?
UserController#update
$user = Auth::user();
$this->validate(request(), [
'name' => 'required',
'username' => 'required',
]);
// Handle File Upload
if(request()->hasfile('profile_pic')){
// Get filename with extension
$fileameWithExt = request()->file('profile_pic')->getClientOriginalName();
// Get just filename
$filename = pathinfo($fileameWithExt, PATHINFO_FILENAME);
// Get just extension
$extension = request()->file('profile_pic')->getClientOriginalExtension();
// Filename to store
$fileNameToStore = $filename . '_' . time() . '.' . $extension;
// Upload Image
$path = request()->file('profile_pic')->storeAs('public/profile_pictures', $fileNameToStore);
} else {
$fileNameToStore = 'noimage.jpg';
}
$user->name = request('name');
$user->username = request('username');
$user->description = request('description');
$user->location = request('location');
if(request()->hasFile('profile_pic')){
$user->profile_pic = $fileNameToStore;
}
$user->push();
$user_id = Auth::user()->id;
return redirect()->route('user', ['id' => $user_id]);
}
I have read the Laravel docs and can't find anything that will quite do what I am looking for. Would appreciate any guidance!
You mean you store username in users, and you want to store the exact username in the author of recipes?
Why not you just reference the name using relationship $recipe->user->username. It would query your users table based on your user_id in your recipes and get that username for you.
So that you're not storing duplicating data in your database. There should be only one Single Source of Truth. You can get your user data based on your user_id, there's no point to store another set of data and keep updating it when the source is changed.
If you find querying whole User model a bit of heavy, then you can use Recipe::with('users:id,username')->get() to query only the username.
Or
If you want to maintain the current $recipe->author, you can:
// Recipe class
public function getAuthorAttribute() {
return $this->user->username;
}
If you set up the foreign keys on your migration files, you may add the ->onUpdate('CASCADE') clause to the username foreign on the recipes table migration.
Note: the onCascade foreign constraint would work outside of Laravel too, as it relies only on the database engine's support for foreign keys.
Anyways, be careful with your validation as you have to be sure the new chosen username isn't already used by someone else.
Assuming your User model is connected to the users table and has an id primary key, make sure that you set the username column as unique in the database, and **validate* user input accordingly.
The former is done by editing once again your migration.
The latter is solved by modifying your rules like these ones:
// Do not forget the Rule import at the top of your controller
use Illuminate\Validation\Rule;
// Then in your method
$this->validate(request(), [
'name' => 'required',
'username' => [
'required',
Rule::unique('users', 'username')->ignore($user)
]
]);
Note: if you modify migrations you have to rerun them in order to apply the modification.
I have a problem. I want to create a dropdown box with the name's from a table in my database andstore just an id from that name in other table (that id is a foreign key). I will show you my code
//acao model
public function estado() {
return $this->belongsTo('App\Estado');
}
//estado model<p>
public function acao()
{
return $this->hasMany('App\Acao');
}
AcaoController#Create:
public function create()
{
$estado = Estado::pluck('nome', 'id');
return view('Backoffice.acoes.criar_acao', compact('estado'));
}
AcaoController#Store:
$data = Acao::create([
'estado_id' => $data[estado_id],
]);
return redirect()->back();
}
This way the store doesn´t work and i think that this code doesn´t work with the relationship beetween acao and estado.
Can anyone help me please
Thanks
If I get it, you want to create an Acao Model among whit the Estado model.
First of all, you are using $data[estado_id] which is not initialized yet.
One simple solution is to create the Acao Object first, then accessing the property estado and create a new Instance of it. then save the model using eloquent:
$acao = new \App\Acao([/*your data*/]); // even a create is good
$acao->save(); // instead of save
$acao->estado()->create(
[/*estado data*/]
);
$acao->save();
I'm not sure what you are trying to achieve passing $data[estado_id] to the create method.
Edit
View
<select name="estado">
#foreach($estdo in $estados)
<option value="{{ $estado->id }}">{{$estado->nome}}</option>
#endforeach
</select>
Controller
Acao::create([
'estado_id' => $request->get('estado'),
]);
Or
$acao = new Acao;
$acao->estado_id = $request->get('estado');
$acao->save();
I have a small (no, not that small) probleme in my current project. Today I came across with Yii and viaTable but something is not working with it. I think something is wrong with the table linking.
My goal would be to get all the data from the client windows(Ablak)
that is connected to a user via felhasznalo2ablak table.
I have 3 tables. Felhasznalo(Users in English), Ablak(Client Window in English) and Felhasznalo2Ablak which is the via table.
Here are the table structures:
Felhasznalo(Model):
public function getWindows() {
return $this->hasMany(Ablak::className(), ['id' => 'ablak_id'])- >viaTable('felhasznalo2ablak',['felhasznalo_id','id']);
}
Ablak(Model):
public function getUsers() {
return $this->hasMany(Felhasznalo::className(), ['id' => 'felhasznalo_id'])->viaTable('felhasznalo2ablak', ['ablak_id' => 'id']);
}
And the query in the controller:
$u = Felhasznalo::findOne(Yii::$app->user->getId());
$allowedWindows = $u->getWindows();
foreach ($allowedWindows as $aw) {
print_r($aw);
}
I want to get the ralational data from Ablak table that blongs to a specific user. It works but not tha way it should. Any ideas guys?
Thank you for your answers!
Gábor
Check the link in your Felhasznalo::getWindows()
public function getWindows() {
return $this
->hasMany(Ablak::className(), ['id' => 'ablak_id'])
->viaTable('felhasznalo2ablak', ['felhasznalo_id' => 'id']);
}
Query for all "Windows"
$u = Felhasznalo::findOne(Yii::$app->user->getId());
$allowedWindows = $u->getWindows()->all();
print_r($allowedWindows);
I forget to answer my thread. So the problem was solved by adding forign keys to my database structure and after that i generated the model files with gii.
Hi I’ve a users and education table. A user can have multiple school or college. So its one to many relationship. education table has school, from_year, to_year and user_id (fk) I want to update the user table as well as education table from a PUT request to users/{id} with email,school, from_year, and to_year fields.
// UsersController.php
public function update(Request $request, $id)
{
$user = User::find($id);
if (!$user) {
return $this->respondNotFound('User not found');
}
$input = $request->all();
$input = array_filter($input, 'strlen');
//$user->update($input);
//Get array of school records
// $user->educatoion->push($records) // don't know what will come here to update the education table
// or may be $user->push(); // don't know
return $this->respond([
'data' => $user,
]);
}
Try to keep it as simple as possible.
If this is your first time updating multiple tables at once, draw up a diagram of the process. This way you can identify the correct order of updates.
Take care to note any formatting that has to done on each value.
Laravel has some great functionality in regards to binding input to a model using ->update($data)
However, when binding to multiple models, you might run into issues with duplicate field names.
Update:
To create a education row from the $user model:
$education = new Education(array('school' => 'Harward', 'from_year' => 1999, 'to_year' => 2016));
User::find($id)->education()->save($education);
I have two models 'User' and 'Profile'.
'email' field is in User model whereas 'name' field is in Profile model.
'profiles' table has a foreign key of 'user_id'.
I searched a lot but couldn't find a proper solution on how I can update both of these entities in one go.
In my ProfileController, I am doing this but I am sure there is a better way. Please help.
public function update($id)
{
$profile = Profile::where('id', $id);
$profile->name = 'Jon';
$profile->save();
$user = User::where('id', $profile->user_id);
$user->email = 'newjon#example.com';
$user->save();
}
My Profile model has
public function user()
{
return $this->belongsTo('User');
}
And my User model has
public function profile()
{
return $this->hasOne('Profile');
}
You can't do it in one go.
However you could simplify it a bit, by leveraging Laravel features, like this (and do it in one-go-like way):
1 Controller edit
$profile = Profile::with('user')->find($id);
// make sure you eager load the user for below to work
2 View
{{ Form::model($profile) }}
{{ Form::text('name') }}
{{ Form::text('user[email]') }}
{{ Form::close() }}
this will autopopulate your profile data (and user data too)
3 Controller update
$profile = Profile::find($id);
$profile->fill(Input::only(.. fields you want to update ..));
$profile->user->fill(Input::get('user')); // array of user data form the form
$profile->push(); // save both models in one go BUT separate queries
Also make sure you have fillable on your models, so fill will does its job.
Another way would be using model events, but I wouldn't do it that way.