Get record real value after overriding it - laravel

I've overridden a a record value like the following :
public function getWeightAttribute($weight)
{
if($weight)
{
return $weight;
}
return 70;
}
Now I have a collection of that model and I want to know if the original value was null or not.
I want to do it without connecting to DB again, Actually I want to make sure the user has filled the weight and some other fields while registering.
Some sections are working based on the above override and I don't want to mess them up neither use extra connections to db.
Thanks In Advance,

In order to get the original value, you can use:-
$fetchData->getAttributes()['weight'];

Related

How to select multiple row values coma separated in laravel

I am trying to get all the ids with coma separated while doing eloquent relationship.
So here is my current queries
Divrank::where('division_id', 591)->with('meta')->orderBy('position', 'asc')->get()
Divrank table has a one to many relation with Divrankmeta model. So with meta I am trying to return
public function meta(){
return $this->hasOne(Divrankmeta::class)->selectRaw('id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed, sum(totalSets) as totalSets, sum(totalGames) totalGames')
->groupBy('divrank_id');
}
So far this query works fine..
I get the result like this screenshot
Ok so in my Divrankmeta model, I have a column called winAgainst and it can have some ids and some left null. So with the meta relation I want to retrieve winAgainst ids with coma separated string inside meta object.
For better understanding, here is how Divrankmeta table looks like
How can I do this?
Thank you.
The relation you created is one-to-one not one-to-many. That's why you are getting a meta object of the first matched row instead of an array that contains all related meta records.
I never put the modification codes into the eloquent functions. Those codes seem belongs to somewhere else. From my perspective, using "resources" and modifying the data there is a better idea.
If you chose the do so:
// Divrank.php
public function metas()
{
return $this->hasMany('App\Models\Divrankmeta');
}
// Divrankmeta.php
public function divrank()
{
return $this->belongsTo('App\Models\Divrank');
}
// DivrankController
public function index()
{
return DivrankResource::collection(Divrank::with("metas")->all());
}
Create a resource file.
php artisan make:resource DivrankResource
Now, you can modify your Divrank collection on the resource file before your controller returns it.
public function toArray($request)
{
$metaIds = [];
forEach($this->metas as $meta) {
array_push($metaIds, $meta['id']);
}
$this['metaIds'] = $metaIds;
return parent::toArray($request);
}
I'm not able to test this code. But it will probably work. If you don't want to use resources, you can create the same functionality in your controller as well. Bu we like to make controllers as short as possible.
Ok I think I solved it, These are the changes I did. Thanks
return $this->hasOne(Divrankmeta::class)
// ->selectRaw('id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed,
// sum(totalSets) as totalSets, sum(totalGames) totalGames')
->select(\DB::raw("id, match_id,divrank_id, sum(won) as won, sum(loss) as loss, sum(draw) as draw, sum(points) as points, sum(matchePlayed) as matchePlayed,
sum(totalSets) as totalSets, sum(totalGames) totalGames, GROUP_CONCAT(winAgainst) as winAgainst"))->groupBy('divrank_id');

How to get the default MySql values in `firstOrNew` in Laravel?

If I use firstOrCreate then the default values are given for the model that I set up in the database. But when I use firstOrNew the values are not given and are instead given out as NULL. Is there any way to fix this aside from using firstOrCreate?
This is because firstOrNew will just create a new instance of your model when it doesn't get a result from the database. Therefore it does not get the default values from the database.
So I guess you have at least two options here. The first one would be the one you already mentioned using firstOrCreate (this is not something I would recommend because this could lead to incorrect state in your database). Another option would be to add 'Accessorson your model with which will return either the value retrieved from the database or when it'snull` it will return the default value you define.
public function getMyFieldAttribute($myField)
{
if ($myField === null) {
return 'my-default-value';
}
return $myField;
}
More info on accessors can be found in the documentation: https://laravel.com/docs/5.2/eloquent-mutators#accessors-and-mutators

Enforce ordering of OData items even when $top is used

I have a DbSet<Items> collection.
The primary key is a Guid. I don't want to order by this primary key. I want to order by an editable decimal property named "Order".
The code I have is very simple, and it works great until the user puts a "$top" parameter into the request:
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
// GET api/documents
[EnableQuery()]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
When the user puts "$top" into the query string, the order gets all messed up (it presumably forces the ordering to be done by the primary key, for consistent paging results -- however, in my situation, this is having the opposite effect, it's preventing me from having consistent paging results).
I've tried moving .AsQueryable() to be earlier in the query (before the .OrderBy(...) clause), I've tried it without the .AsQueryable(), I've tried it with two AsQueryables, etc.
There are going to be a lot of items in this table, so it needs to be done via an IQueryable (enumerating all of the items on the web server via IEnumerable is not an option here).
The only thing that has worked so far is passing in "$orderby=Order" from the client, but I don't want to force that (seems like it will get forgotten easily).
1.) Is there anything I can do to make ordering by my Order property the default behavior here?
2.) Or failing that, is there anyway to trick WebApi / OData into thinking that a custom "$orderby=Order" clause was specified?
To override default sort order, you need to set property EnsureStableOrdering of EnableQueryAttribute to false, like describe here:
A true value indicates the original query should be modified when
necessary to guarantee a stable sort order. A false value indicates
the sort order can be considered stable without modifying the query.
Query providers that ensure a stable sort order should set this value
to false. The default value is true.
So in your code, changes the action attribute like this:
// GET api/documents
[EnableQuery(EnsureStableOrdering = false)]
public IQueryable<Item> Get()
{
return ctx.Items.OrderBy(o => o.Order).AsQueryable();
}
You can manually invoke the odata in your controller. This should create the proper sorted IQueryable and then apply the $top and any other odata like $filter and $skip. Now you don't have to return an IQueryable which was causing the problem because the actual query was being executed later in the pipeline.
public class ItemsController : ApiController
{
protected DbContext ctx = // ...
public IEnumerable<Item> Get(ODataQueryOptions<Item> odata)
{
var collection = ctx.Items.OrderBy(o => o.Order);
if (odata == null)
{
//return a default max size of 100
return collection.Take(100).ToList();
}
var results = odata.ApplyTo(collection.AsQueryable()) as List<Item>;
//still provide a max incase the $top wasn't specified.
//you could check the odata to see if $top is there or not.
return results.Take(100);
}
}
More information can be found in the WebApi documentation.

Is there a generic way to assign a new object's value to an original object's value of the same class?

I have an Original Document object which has subclasses and properties. I have a different object, which is a subset of this object which holds values entered from a form, a view model if you like. Since both object are from the same class the structure is identical.
Is there a way to generically assign values from the new object to the original object? My current approach is explicit:
myOrigDoc.Introduction.Name = myDoc.Introduction.Name;
myOrigDoc.Introduction.Clients[0].Firstname =
myDoc.Introduction.Clients[0].Firstname;
Also I want to ensure that only the properties with values (not null) are assigned.
Can this be done?
Many thanks in advance,
Ed
EDIT: Experimentation with ValueInjector, and its class to prevent nulls being assigned.
public class StrNotNull: ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
}
//protected override object SetValue(ConventionInfo c)
//{
// return c.SourceProp.Value.ToString();
//}
}
The code that calls this:
myOrigDoc.InjectFrom<StrNotNull>(myDoc);
EDIT2: Possible code for Automapper use. Although not sure as yet about ignoring nulls, and thus really doing a merge.
Mapper.CreateMap<Document, Document>();
myOrigDoc = Mapper.Map<Document, Document>(myDoc);
Thanks in advance for any advice and help.
You can use AutoMapper, the object-to-object mapper for this task. The library and documentation can be found at http://automapper.org/
ValueInjecter may solve the problem you need

Filter every call made by a DataContext when using LinQ Entities

I'm using logical delete in my system and would like to have every call made to the database filtered automatically.
Let say that I'm loading data from the database in the following way :
product.Regions
How could I filter every request made since Regions is an EntitySet<Region> and not a custom method thus not allowing me to add isDeleted = 0
So far I found AssociateWith but I'd hate to have to write a line of code for each Table -> Association of the current project...
I'm looking into either building generic lambda Expressions or.. something else?
You could create an extension method that implements your filter and use that as your convention.
public static class RegionQuery
{
public static IQueryable<Region> GetAll(this IQueryable<Region> query, bool excludeDeleted=true)
{
if (excludeDeleted)
return query.Regions.Where(r => !r.isDeleted);
return query.Regions;
}
}
So whenever you want to query for regions you can make the following call to get only the live regions still providing an opportunity to get at the deleted ones as well.
context.Regions.GetAll();
It my be a little wonky for access the Products property, but still doable. Only issue is you would have to conform to the convention. Or extend the containing class.
someProduct.Regions.GetAll();
I hope that helps. That is what I ended up settling on because I haven't been able to find a solution to this either outside of creating more indirection. Let me know if you or anyone else comes up with a solution to this one. I'm very interested.
It looks to me like you're using a relationship between your Product and Region classes. If so, then somewhere, (the .dbml file for auto-generated LINQ-to-SQL), there exists a mapping that defines the relationship:
[Table(Name = "Product")]
public partial class Product
{
...
private EntitySet<Region> _Regions;
[Association(Storage = "_Regions")]
public EntitySet<Region> Regions
{
get { return this._Regions; }
set { this._Regions.Assign(value); }
}
...
}
You could put some logic in the accessor here, for example:
public IEnumerable<Region> Regions
{
get { return this._Regions.Where(r => !r.isDeleted); }
set { this._Regions.Assign(value); }
}
This way every access through product.Regions will return your filtered Enumerable.

Resources