Where to put model code in ORM ? (Eloquent) - php

I dont quite understand where my code goes for this orm.
class Brand_model extends MY_Model {
public function add_brand($name, $size)
{
//goal:
// $sql = "insert into brand (name, size_id) values (?,?)";
// $query = $this->db->query($sql, array($name, $size));
$brand = new self();
$brand->name=$name;
$brand->size=$size;
$brand->save();
}
This produces a new row in the database, in the appropriate table, but with no data inside of it. However I am sure those variables are filled. Any ideas?
My design pattern pre orm is to put almost everything in the model. That way if multiple controllers need the same data structure, i call a function once and it handles all the validation/etc.
THanks!

Please find the link for eloquent and codeigniter integration
http://mannyisles.com/using-eloquent-orm-inside-codeigniter.html

To use the ORM, first of all, you would need the connection set up. You can look into this answer. It tells you how to set up eloquent DB connection.
Once it is done, you need to create a model, which would extends the ORM, and do all your DB calls using that model.
You can create a file - let's say, FooModel.php like this:
<?php
namespace Models\FooModel;
use Illuminate\Database\Eloquent\Model;
class FooModel extends Model
{
protected $table = 'sample_table';
protected $fillable = array('comma', 'seperated', 'column', 'names');
protected $hidden = array('id');
public $timestamps = true;
}
?>
$fillable tells eloquent which columns you want to write into. you can ignore the auto incremented columns and the columns with default values.
$timestamps handle your created_at and updated_at columns, if you have any.
When you have to create the row, you can call the eloquent create() function. This function requires an associative array with column names as key and values as the corresponding value you wanna insert to that column.
$row = array('name' => 'Foo',
'email' => 'foo#bar.com'
);
You can then just call the create function, with $row as a parameter.
$response = FooModel::create($row);
If you do a var_dump($response); you can see the status of the create(). true for success or the error message.
For further info you can check out the Docs. It's really not that hard!
Cheers! :)

Related

How do I return my OUTPUT clause in Laravel DB::insert

I am using Laravel and sqlsrv, connected to SQL Server 2016 and all is working great until I try to use an output clause in my insert query.
Query is something like
INSERT INTO TABLE(Columns) OUTPUT INSERTED.MyDesiredReturnColumn VALUES(Value)
This is working perfectly in SQL Server, and returning the desired value, but using Laravel's DB::insert functionality it is only returning a 1 (for successful insert)
I have a workaround that I would rather not have right now, using the CreatedOn field to return the most recently created row, but this has potential issues.
UPDATES: The field I am attempting to retrieve is a uniqueidentifier field (guid) that is created in SQL, not from Laravel-side
After attempting #PrathameshPalav's recommendation of using the Eloquent model creation, the values are being inserted correctly into the DB, but it is not returning the uniqueidentifier still.
$inserted = MyModel::create($information);
print "inserted id is " . $inserted->MyModelId;
This is printing "inserted id is "
Here is my model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class MyModel extends Model
{
//
protected $table = 'MyModelBase';
protected $primaryKey = 'MyModelId';
public $incrementing = false;
protected $keyType = "string";
public $timestamps = false;
protected $fillable = ['field1', 'field2', 'etc'];
}
Any ideas would be greatly helpful.
You can use Eloquent ORM for this purpose:
$insertedObject = ModelName::create($input_array);
It will return inserted model object in response. Or if you want only inserted record id then use
DB::table($tablename)->insertGetId($input_array);
The way that I solved this was by incorporating an Eloquent model (as pointed out by #PrathameshPalav), then (loosely) following this tutorial https://danielkoch.work/log/laravels-eloquent-guids.html
Specifically this part
public static function boot() {
parent::boot();
// Hook when a model is created
static::creating(function ($model) {
// Select a new ID
$result = DB::select( DB::raw('Select NewID() NewUUID') );
$model->{$model->getKeyName()} = $result[0]->NewUUID;
});
}
After that, I added the primary key I had defined to the $fillable array and tested, and it works =)
Thank you both for your help!
Yes, when you use insert it will return a bool. You can use insertGetId to get the the id.
If the table has an auto-incrementing id, use the insertGetId method
to insert a record and then retrieve the ID:
$data = [....]; // data that will be inserted
$id = DB::table('xxx')->insertGetId($data);
More info:
https://laravel.com/docs/5.5/queries#inserts

Laravel 5 - Override Get or Select from Eloquent

Is it possible to override the ->get() methods from Eloquent in order to always customize the output of the selected fields from the DB query?
For example, if you do a usual Eloquent query like:
User::where('email','like','%wherever.com')->get();
Instead of it making the query:
Select name, email, address, created_at from users where email like '%wherever.com'
Is it possible to override the method to always return something like:
Select CONCAT('CL::',name), lower(email), address, created_at from users where email like '%wherever.com'
I ask for ->get because I have seen that I can pass an array with the columns to be selected but I don't want to define them on all queries.
So, after digging around the functions regarding the query I found this solution:
I created a new class CustomQueryBuilder that extends the Illuminate\Database\Query\Builder
There you can override the get() / select() / where() methods from Eloquent.
Then, in the models that you want to change the way the query is made, define the fields to change like:
protected $encrypted = [
'name',
'email',
];
After this, I created a new class CustomModel that extends the Illuminate\Database\Eloquent\Model and there override the newBaseQueryBuilder like this:
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new CustomQueryBuilder(
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor(), $this
);
}
Inside the CustomQueryBuilder you can customise all the methods from builder for your needs.
With this setup, you can in any Illuminate\Database\Eloquent\Model change it to extend your CustomModel and inherit this special behaviour for the designated columns.
Be aware that all queries made from Models extending your CustomModel will get this new methods, so do all the needed checks to don't mess up with Eloquent normal behaviour, something like this:
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($this->model !== null && isset($this->model::$encrypted))
{
if (in_array($column, $this->model::$encrypted))
{
$column = DB::raw("CONCAT('CL::',$column)");
}
}
parent::where($column, $operator, $value, $boolean);
}
PS: I know this sound silly with the CONCAT example but with the property $encrypted you can figure out that it's not for concatenating string.
You can use model to update the result for the specific field like below
Use this code in your model file to contact CL::' with thename` value
public function getNameAttribute($value) {
return 'CL::'.$value;
}
You can just call User::where('email','like','%wherever.com')->get(); like this
This getNameAttribute function will always return name value with "CL::"
So you don't need to add CONCAT('CL::'.name) with your query.
Same way you can add for other fields also
Updated
Solution when querying the result
Add this geofields in your model
protected $geofields = array("concat('CL::',name) as name");
Add this newQuery function to override the columns
public function newQuery($excludeDeleted = true)
{
$raw='';
foreach($this->geofields as $column){
$raw .= $column;
}
return parent::newQuery($excludeDeleted)->addSelect('*',\DB::raw($raw));
}
Hope this is what you expect.

Laravel Removing Pivot data in many to many relationship

Not sure if I set this up correctly. In Laravel I'm creating two models with a many-to-may relationship
The models are Item and Tags. Each one contains a belongsTo to the other.
When I run a query like so:
Item::with('tags')->get();
It returns the collection of items, with each item containing a tags collection. However the each tag in the collection also contains pivot data which I don't need. Here it is in json format:
[{
"id":"49",
"slug":"test",
"order":"0","tags":[
{"id":"3","name":"Blah","pivot":{"item_id":"49","tag_id":"3"}},
{"id":"13","name":"Moo","pivot":{"item_id":"49","tag_id":"13"}}
]
}]
Is there anyway to prevent this data from getting at
you can just add the name of the field in the hidden part in your model like this:
protected $hidden = ['pivot'];
that's it , it works fine with me.
You have asked and you shall receive your answer. But first a few words to sum up the comment section. I personally don't know why you would want / need to do this. I understand if you want to hide it from the output but not selecting it from the DB really has no real benefit. Sure, less data will be transferred and the DB server has a tiny tiny bit less work to do, but you won't notice that in any way.
However it is possible. It's not very pretty though, since you have to override the belongsToMany class.
First, the new relation class:
class BelongsToManyPivotless extends BelongsToMany {
/**
* Hydrate the pivot table relationship on the models.
*
* #param array $models
* #return void
*/
protected function hydratePivotRelation(array $models)
{
// do nothing
}
/**
* Get the pivot columns for the relation.
*
* #return array
*/
protected function getAliasedPivotColumns()
{
return array();
}
}
As you can see this class is overriding two methods. hydratePivotRelation would normally create the pivot model and fill it with data. getAliasedPivotColumns would return an array of all columns to select from the pivot table.
Now we need to get this integrated into our model. I suggest you use a BaseModel class for this but it also works in the model directly.
class BaseModel extends Eloquent {
public function belongsToManyPivotless($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null){
if (is_null($relation))
{
$relation = $this->getBelongsToManyCaller();
}
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey();
if (is_null($table))
{
$table = $this->joiningTable($related);
}
$query = $instance->newQuery();
return new BelongsToManyPivotless($query, $this, $table, $foreignKey, $otherKey, $relation);
}
}
I edited the comments out for brevity but otherwise the method is just like belongsToMany from Illuminate\Database\Eloquent\Model. Of course except the relation class that gets created. Here we use our own BelongsToManyPivotless.
And finally, this is how you use it:
class Item extends BaseModel {
public function tags(){
return $this->belongsToManyPivotless('Tag');
}
}
If you want to remove pivot data then you can use as protected $hidden = ['pivot']; #Amine_Dev suggested, so i have used it but it was not working for me,
but the problem really was that i was using it in wrong model so i want to give more detail in it that where to use it, so you guys don't struggle with the problem which i have struggled.
So if you are fetching the data as :
Item::with('tags')->get();
then you have to assign pivot to hidden array like below
But keep in mind that you have to define it in Tag model not in Item model
class Tag extends Model {
protected $hidden = ['pivot'];
}
Two possible ways to do this
1. using makeHidden method on resulting model
$items = Item::with('tags')->get();
return $items->makeHidden(['pivot_col1', 'pivot_col2']...)
2. using array_column function of PHP
$items = Item::with('tags')->get()->toArray();
return array_column($items, 'tags');

Laravel Eloquent::Find() returning NULL with an existing ID

It's pretty straightforward as it's the most basic thing but I don't know what I'm missing:
Having a model called Site
I'm using Eloquent ORM, so when I call (in a controller)
$oSite = Site::find(1)
and then
var_dump($oSite);
It returns a value of NULL.
But when I check the database, the table 'sites' actually contains the following item:
id: 1
user_id: 1
name: test
In my Site model I have the following code:
use Illuminate\Database\Eloquent\ModelNotFoundException;
Class Site extends Eloquent {
protected $table = 'sites';
protected $fillable = ['user_id', 'name'];
}
Instead, if I gather the item with the following:
$oSite = DB::table('sites')
->where('id', 1)
->first();
It works and I get the correct register.
What I'm doing wrong? Which part of the documentation I didn't get?
EDIT:
Model code can be checked above.
Controller:
use Illuminate\Support\Facades\Redirect;
class SiteManagementController extends BaseController {
...
public function deleteSite()
{
if (Request::ajax())
{
$iSiteToDelete = Input::get('siteId');
$oSite = Site::find($iSiteToDelete);
return var_dump($oSite);
}
else
{
return false;
}
}
}
EDIT 2: (SOLVED)
Real reason why wasn't working:
I had originally in my model code the following:
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Illuminate\Database\Eloquent\ModelNotFoundException;
Class Site extends Eloquent {
protected $table = 'sites';
use SoftDeletingTrait;
protected $dates = ['deleted_at'];
protected $fillable = ['user_id', 'name'];
}
Problem was I added a 'deleted_at' column after I started the project and when I applied migrations, I didn't have softdeleting enabled.
Obviously, I did a second error, forgetting to enable 'deleted_at' to be nullable, hence all inserts went had a wrong timestamp (0000-00-00 ...).
Fix:
Made nullable 'deleted_at' column.
Set all wrong 'deleted_at' timestamps to NULL.
Check you are getting Input::get('siteId') correctly. if you are getting it then try to convert it into integer i.e
$iSiteToDelete = intval(Input::get('siteId'));
You're not returning your model.
var_dump prints output and returns nothing.
do this instead:
dd($oSite); // stands for var_dump and die - a helper method
and even better, simply return the model:
return $oSite; // will be cast to JSON string
In my case I was using a custom query with the DB facade. I neglected to skip records that have a deleted_at in my DB query. When showing all the records, it worked with IDs that had already been deleted, so methods like find that if they were considering the deleted_at, did not find the record.
Layer eight.
For the future if you encounter a similar problem you can check what SQL queries laravel is sending to the database.
Todo so just enable query logging by using DB facade:
\DB::enableQueryLog();
Before sending request to database.
Then after using find() or get() you can get all requests by:
\DB::getQueryLog();
You can getQueryLog into dd() function and see what database queries were made.

Laravels Eloquent ORM: setting datatypes of the model

Currently I have a simple table with two rows: id and key.
Whenever I try to get my data from the database with City::get() the response contains id columns in the string format.
Is there a simple way/package how I can define the data formats for each of my columns? E.g. - id in this example should have been an integer.
Model:
<?php
class City extends Eloquent {
protected $table = 'cities';
protected $primaryKey = 'Id';
}
Controller:
class CityController extends \BaseController {
public function index()
{
var_export(is_integer(City::get()->first()->Id));
var_export(is_string(City::get()->first()->Id));
die;
}
}
Output:
false
true
Eloquent Models have a property casts that can be used to hint at the type for each model attribute.
5.0: https://laravel.com/docs/5.0/eloquent#attribute-casting
5.4: https://laravel.com/docs/5.4/eloquent-mutators#attribute-casting
This was my case using eloquent for a legacy application locked to PHP 5.4.
For some random reason i didn't try to figure out, every attribute was retrieved as a string from the database and the default PHP casting was causing me problems.
Your code would be:
<?php
class City extends Eloquent {
protected $table = 'cities';
protected $primaryKey = 'Id';
protected $casts = [
'Id' => 'int'
];
}
Every field from a record that comes out of the database is going to be a string.
This is just how many db extensions in PHP seem to work.
Ruby on Rails keeps a constant map of the schema to know that tableA.field1 is an integer, so it can convert anything to an int when it fetches the database. This obviously has some overhead to it, but it can be a useful feature. Laravel opted to not do this in the interest of performance over convenience.
You can use accessors and mutators to manually replicate this functionality.
Are you sure that the data type in the database is an integer (INT) as well?
Else you could, maybe, convert the string to an integer. For example:
$num = "4.5";
$int = (int)$num;
return gettype($int); // Integer

Categories