I have tried to get the auto increment ID from the booking table before saving the new data's, but I have errors all the time.
This is the code in the controller:
public function addAppointment(Request $request) {
$user = auth()->user();
$booking = new Booking();
$booking->vac_center_id = $request->get('vaccination_center');
$booking->vac_id = $request->get('vaccination_id');
$booking->date_of_shot = $request->get('date_of_shot');
$booking->time = $request->get('time');
$booking->shot_number = $request->get('shot_number');
$booking->isDone = 0;
$booking->isCancelled = 0;
$booking->user_id = $user->id;
$booking->save();
$booking = new BookingHasVaccinationCenters();
//here below I want to get the auto increment id
$booking->booking_id->id;
$booking->vac_center_id = $request->get('vaccination_center');
$booking->save();
return redirect('/home');
}
This is the error that I had last time when I try to do this:
Attempt to read property "id" on null
instead of this
$booking = new BookingHasVaccinationCenters();
//here below I want to get the auto increment id
$booking->booking_id->id;
$booking->vac_center_id = $request->get('vaccination_center');
$booking->save();
use below code
$bookingHasVaccination = new BookingHasVaccinationCenters();
//change here
$bookingHasVaccination->booking_id = $booking->id;
$bookingHasVaccination->vac_center_id = $request->get('vaccination_center');
$bookingHasVaccination->save();
Note : always try to define variable with the name same as model class while crud operations
Your error is that you declare with the same name the variable $booking , when you
save the $booking you should declare a instance of object with other name for example
$bookingvaccine->booking_id = $booking->id;
I know your questions has already been answered, but let me share another way of doing what you are doing, so you prevent this errors and your code is better.
If you have relations between this tables/models (relations functions created) then you can use the relation to create new models between them, without the need of sharing or passing the parent model's ID.
Assuming your User's relation name with Booking is bookings and for Booking -> BookingHasVaccinationCenters relation (strange name) is bookingHasVaccinationCenters, you should be able to do this:
public function addAppointment(Request $request)
{
$booking = $request->user()
->booking()
->create([
'vac_center_id' => $request->input('vaccination_center'),
'vac_id' => $request->input('vaccination_id'),
'date_of_shot' => $request->input('date_of_shot'),
'time' => $request->input('time'),
'shot_number' => $request->input('shot_number'),
'isDone' => false,
'isCancelled' => false,
]);
$booking->bookingHasVaccinationCenters()->create([
'vac_center_id' => $request->input('vaccination_center'),
]);
return redirect('/home');
}
Another super small tip, remember to cast isDone and isCancelled to boolean, so you can use those fields as boolean so you can do true or false instead of 1 or 0.
And last tip, try to always stick to the Laravel's conventions: snake_case column names, isDone and isCancelled should be is_done and is_cancelled.
Is there a way to invoke eloquent relationship methods without changing the original eloquent collection that the method runs on? Currently I have to employ a temporary collection to run the method immutable and to prevent adding entire related record to the response return:
$result = Item::find($id);
$array = array_values($result->toArray());
$temp = Item::find($id);
$title = $temp->article->title;
dd($temp); //This prints entire article record added to the temp collection data.
array_push($array, $title);
return response()->json($array);
You are not dealing with collections here but with models. Item::find($id) will get you an object of class Item (or null if not found).
As far as I know, there is no way to load a relation without storing it in the relation accessor. But you can always unset the accessor again to delete the loaded relation (from memory).
For your example, this process yields:
$result = Item::find($id);
$title = $result->article->title;
unset($result->article);
return response()->json(array_merge($result->toArray(), [$title]));
The above works but is no very nice code. Instead, you could do one of the following three things:
Use attributesToArray() instead of toArray() (which merges attributes and relations):
$result = Item::find($id);
return response()->json(array_merge($result->attributesToArray(), [$result->article->title]));
Add your own getter method on the Item class that will return all the data you want. Then use it in the controller:
class Item
{
public function getMyData(): array
{
return array_merge($this->attributesToArray(), [$this->article->title]);
}
}
Controller:
$result = Item::find($id);
return response()->json($result->getMyData());
Create your own response resource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ItemResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'title' => $this->article->title,
'author' => $this->article->author,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
Which can then be used like this:
return new ItemResource(Item::find($id));
The cleanest approach is option 3. Of course you could also use $this->attributesToArray() instead of enumerating the fields, but enumerating them will yield you security in future considering you might extend the model and do not want to expose the new fields.
I see two ways you can achieve that.
First, you can use an eloquent Resource. Basically it'll allow you to return exactly what you want from the model, so in your case, you'll be able to exclude the article. You can find the documentation here.
The second way is pretty new and is still undocumented (as fas i know), but it actually works well. You can use the unsetRelation method. So in your case, you just have to do:
$article = $result->article; // The article is loaded
$result->unsetRelation('article'); // It is unloaded and will not appear in the response
You can find the unsetRelation documentation here
There is not as far as I know. When dealing with Model outputs, I usually construct them manually like this:
$item = Item::find($id);
$result = $item->only('id', 'name', 'description', ...);
$result['title'] = $item->article->title;
return $result;
Should you need more power or a reusable solution, Resources are your best bet.
https://laravel.com/docs/5.6/eloquent-resources#concept-overview
Suppose I have a relationship between the following two Models in Laravel's Eloquent:
<?php
// user:
// - user_id
class User extends Model
{
protected $table = 'users';
public function settings()
{
return $this->hasMany('Setting');
}
public function settingSet($key, $value)
{
\Setting::setConfigItem($key, $value, $this->user_id);
}
}
// settting:
// - setting_key
// - setting_value
// - user_id
class Setting extends Model
{
public function setConfigItem($key, $value, $user_id)
{
// Note: I've provided this code here as an example, so it should
// exist here only as pseudo-code - it has not been tested and
// is outside the scope of this issue but has been requested by
// a commenter so I've provided the basis for this method:
$existing = \Setting::where(['key' => $key, 'user_id' => $user_id])->first();
if (!$existing) {
\Setting::insert([ 'setting_key' => $key, 'setting_value' => $value, 'user_id' => $user_id ]);
} else {
$existing->setting_value = $value;
$existing->save();
}
}
}
And I want to retrieve a single user and his settings, I can do the following:
<?php
$user = User::with(['setting'])->find(1);
Now, with this user, I can update or insert a setting using the settingSet method, as listed above.
<?php
$user->settingSet('foo','bar');
However, if I retrieve the settings at this point, I will get stale data.
<?php
print_r($user->settings); // onoes!
What's the best practice to force the data for this relationship to be updated after an INSERT/UPDATE/DELETE in the User::settingSet method or in other similar methods?
You can force the data to be updated by using Lazy Eager Loading, load() function.
print_r($user->load('settings'));
source: http://laravel.com/docs/5.0/eloquent#eager-loading
You have this issue due to using query builder instead of the eloquent,I dont understand why your using both,if you're using eloquent then use eloquent if you're using query builder use query builder,but dont use both,at least not when you have the possibility not to.
I find the setConfigItem method useless as you arent pushing a user into a setting but a setting into a user so basically all implementions should be on a user class and not on the settings class
After clearing that out,you could try doing something like this -
public function settingSet($key, $value)
{
$setting = new Setting([
'setting_key' => $key,
'setting_value' => $value
]);
$this->settings()->save($setting);
}
also you could improve this method by instead of accepting just 1 setting at a time you could accept array of settings
btw is there a reason why you arent using pivot table ? are the settings unique foreach user ?
I have a class called Vara, where i have a table field called searchname. I want to do a simple setup of cviebrock eloquent sluggable but can't figure out what the issue is.
When i save my model, nothing happens, it rewrite the old value stored.
If i change in build_from to, whatthefuckisgoingon i get the same output. I have a field called handle, also tried changing the field namne to slug but same result. If i leave build_from empty i also get the same output.
If i however change save_to to something that doesn't exist i get an error. The searchname field does have a value of "Hjordnära test 33 liter", so the output is really wierd.
My guess is that build_from is being ignored, and seen as null. How do i fix this?
My Vara.php looks like this
use Cviebrock\EloquentSluggable\SluggableInterface;
use Cviebrock\EloquentSluggable\SluggableTrait;
class Vara extends \Eloquent implements SluggableInterface {
use SluggableTrait;
protected $sluggable = array(
'build_from' => 'searchname',
'save_to' => 'handle'
);
In my VarorController.php
public function saveVara()
{
$id = Input::get('id');
$vara = Vara::find(Input::get('id'));
$vara->edited_by = Auth::user()->id;
$vara->searchname = Input::get('searchname');
$vara->save();
return $vara->getSlug();
Ok a litle update, found this function in SluggableTrait.php
public function sluggify($force=false)
{
$config = \App::make('config')->get('eloquent-sluggable::config');
$this->sluggable = array_merge( $config, $this->sluggable );
if ($force || $this->needsSlugging())
{
$source = $this->getSlugSource();
$slug = $this->generateSlug($source);
$slug = $this->validateSlug($slug);
$slug = $this->makeSlugUnique($slug);
$this->setSlug($slug);
}
return $this;
}
so if i add $vara->sluggify(true); to my controller the slug is being saved, so now the questions is why it does not sluggify automaticly on $vara->save();
Most probably, it's an issue of validation because you're using Ardent:
Ardent is a package that "provides self-validating smart models for Laravel Framework 4's Eloquent ORM"
Check your rules and use if statement:
if(! $vara->save()) // if model is invalid
dd($vara->errors());
If you don't need to check validation , you may use
$vara->forceSave();
To integrate Eloquent sluggable with Ardent, take a look at this link
What's the shorthand for inserting a new record or updating if it exists?
<?php
$shopOwner = ShopMeta::where('shopId', '=', $theID)
->where('metadataKey', '=', 2001)->first();
if ($shopOwner == null) {
// Insert new record into database
} else {
// Update the existing record
}
Here's a full example of what "lu cip" was talking about:
$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();
Below is the updated link of the docs which is on the latest version of Laravel
Docs here: Updated link
2020 Update
As in Laravel >= 5.3, if someone is still curious how to do so in easy way it's possible by using: updateOrCreate().
For example for the asked question you can use something like:
$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);
Above code will check the table represented by ShopMeta, which will be most likely shop_metas unless not defined otherwise in the model itself.
And it will try to find entry with
column shopId = $theID
and
column metadateKey = 2001
and if it finds then it will update column shopOwner of found row to New One.
If it finds more than one matching rows then it will update the very first row that means which has lowest primary id.
If not found at all then it will insert a new row with:
shopId = $theID,metadateKey = 2001 and shopOwner = New One
Notice
Check your model for $fillable and make sure that you have every column name defined there which you want to insert or update and rest columns have either default value or its id column auto incremented one.
Otherwise it will throw error when executing above example:
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field '...' doesn't have a default value (SQL: insert into `...` (`...`,.., `updated_at`, `created_at`) values (...,.., xxxx-xx-xx xx:xx:xx, xxxx-xx-xx xx:xx:xx))'
As there would be some field which will need value while inserting new row and it will not be possible, as either it's not defined in $fillable or it doesn't have a default value.
For more reference please see Laravel Documentation at:
https://laravel.com/docs/5.3/eloquent
One example from there is:
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
which pretty much clears everything.
Query Builder Update
Someone has asked if it is possible using Query Builder in Laravel. Here is reference for Query Builder from Laravel docs.
Query Builder works exactly the same as Eloquent so anything which is true for Eloquent is true for Query Builder as well. So for this specific case, just use the same function with your query builder like so:
$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);
Of course, don't forget to add DB facade:
use Illuminate\Support\Facades\DB;
OR
use DB;
Updated: Aug 27 2014 - [updateOrCreate Built into core...]
Just in case people are still coming across this... I found out a few weeks after writing this, that this is in fact part of Laravel's Eloquent's core...
Digging into Eloquent’s equivalent method(s). You can see here:
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553
on :570 and :553
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return static
*/
public static function updateOrCreate(array $attributes, array $values = array())
{
$instance = static::firstOrNew($attributes);
$instance->fill($values)->save();
return $instance;
}
Old Answer Below
I am wondering if there is any built in L4 functionality for doing this in some way such as:
$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();
I did create this method a few weeks back...
// Within a Model extends Eloquent
public static function createOrUpdate($formatted_array) {
$row = Model::find($formatted_array['id']);
if ($row === null) {
Model::create($formatted_array);
Session::flash('footer_message', "CREATED");
} else {
$row->update($formatted_array);
Session::flash('footer_message', "EXISITING");
}
$affected_row = Model::find($formatted_array['id']);
return $affected_row;
}
I would love to see an alternative to this if anyone has one to share.
firstOrNew will create record if not exist and updating a row if already exist.
You can also use updateOrCreate here is the full example
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
If there's a flight from Oakland to San Diego, set the price to $99. if not exist create new row
Reference Doc here: (https://laravel.com/docs/5.5/eloquent)
Save function:
$shopOwner->save()
already do what you want...
Laravel code:
// If the model already exists in the database we can just update our record
// that is already in this database using the current IDs in this "where"
// clause to only update this model. Otherwise, we'll just insert them.
if ($this->exists)
{
$saved = $this->performUpdate($query);
}
// If the model is brand new, we'll insert it into our database and set the
// ID attribute on the model to the value of the newly inserted row's ID
// which is typically an auto-increment value managed by the database.
else
{
$saved = $this->performInsert($query);
}
If you need the same functionality using the DB, in Laravel >= 5.5 you can use:
DB::table('table_name')->updateOrInsert($attributes, $values);
or the shorthand version when $attributes and $values are the same:
DB::table('table_name')->updateOrInsert($values);
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));
Then make your changes and save. Note the firstOrNew doesn't do the insert if its not found, if you do need that then its firstOrCreate.
Like the firstOrCreate method, updateOrCreate persists the model, so there's no need to call save()
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99]
);
And for your issue
$shopOwner = ShopMeta::updateOrCreate(
['shopId' => $theID, 'metadataKey' => '2001'],
['other field' => 'val' ,'other field' => 'val', ....]
);
One more option if your id isn't autoincrement and you know which one to insert/update:
$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();
Actually firstOrCreate would not update in case that the register already exists in the DB.
I improved a bit Erik's solution as I actually needed to update a table that has unique values not only for the column "id"
/**
* If the register exists in the table, it updates it.
* Otherwise it creates it
* #param array $data Data to Insert/Update
* #param array $keys Keys to check for in the table
* #return Object
*/
static function createOrUpdate($data, $keys) {
$record = self::where($keys)->first();
if (is_null($record)) {
return self::create($data);
} else {
return self::where($keys)->update($data);
}
}
Then you'd use it like this:
Model::createOrUpdate(
array(
'id_a' => 1,
'foo' => 'bar'
), array(
'id_a' => 1
)
);
like #JuanchoRamone posted above (thank #Juancho) it's very useful for me, but if your data is array you should modify a little like this:
public static function createOrUpdate($data, $keys) {
$record = self::where($keys)->first();
if (is_null($record)) {
return self::create($data);
} else {
return $record->update($data);
}
}
Isn't this the same as updateOrCreate()?
It is similar but not the same. The updateOrCreate() will only work
for one row at a time which doesn't allow bulk insert.
InsertOnDuplicateKey will work on many rows.
https://github.com/yadakhov/insert-on-duplicate-key
Try more parameters one which will surely find and if available update and not then it will create new
$save_data= Model::firstOrNew(['key1' => $key1value,'key'=>$key2value]);
//your values here
$save_data->save();
UpdateOrCreate method means either update or creates by checking where condition.
It is simple as in the code you can see, in the users table, it will check if an email has the value $user->email then it will update the data (which is in the 2nd param as an array) or it will create a data according to it.
$newUser = User::updateOrCreate(['email' => $user->email],[
'name' => $user->getName(),
'username' => $user->getName().''.$user->getId(),
'email' => $user->getEmail(),
'phone_no' => '',
'country_id' => 0,
'email_verified_at' => Carbon::now()->toDateTimeString(),
'is_email_verified' => 1,
'password'=>Hash::make('Secure123$'),
'avatar' => $user->getAvatar(),
'provider' => 'google',
'provider_id' => $user->getId(),
'access_token' => $user->token,
]);
check if a user exists or not. If not insert
$exist = DB::table('User')->where(['username'=>$username,'password'=>$password])->get();
if(count($exist) >0) {
echo "User already exist";;
}
else {
$data=array('username'=>$username,'password'=>$password);
DB::table('User')->insert($data);
}
Laravel 5.4