Ability to tell if a model for CRUD controller has been updated using Backpack for Laravel - laravel-backpack

I'm using a Backpack for Laravel CRUD controller and I'm trying to figure out if there's an efficient way to tell if a Model has been updated without loading both the current model and the updated model and then comparing attribute values.

I ended up following the suggestion in the comment and overrode the save() method on my model as such.
public function save(array $options = [])
{
// Update the mode accordingly and perform any
// other actions you need to prior to saving here.
$saved = parent::save($options);
return $saved;
}

Related

How can I use Spatie Media Library with Backpack for Laravel upload field?

I want to upload a file in Backpack for Laravel and immediately attach the file to the model using Spatie Media Library.
What is my best option to do this?
I tried doing this using the SetImageAttribute mutator as recommended in the docs: https://backpackforlaravel.com/docs/4.1/crud-fields#upload-1
public function setImageAttribute($value) {
$this->addMedia('image');
}
However this method requires me to have an unused "image" field on the DB (because Spatie Media Library uses its own table).
Is there another way to do this without using a mutator/creating extra DB fields?
You can avoid having to create an unused image field on your DB by overwriting both the set and get mutators/accessor on your model.
Laravel documentation defining accessor
Laravel documentation defining mutator
Create the following 2 methods on your model:
public function getImageAttribute()
{
return $this->getFirstMediaUrl('image') ?? '';
}
public function setImageAttribute($file)
{
$this->clearMediaCollection('image'); // to remove any previous files
if ($file) {
$this->addMedia($file)->usingFileName($file->hashName())->toMediaCollection('image');
}
}
If you have a fillable set on your model, also add image to it.
protected $fillable = ['image']
Now you should be able to create and update the files using Backpack.
You may change the implementation based on your needs and requirements.

Shopware 6 Forms

Newbie question. How to handle Storefront Form Submit in Shopware 6? How to save the data from form to database? I have an entity, form shown in storefront and a controller but i have no idea how to save the data to entity. Thanks in advance.
you would have to be more specific with the description, of what exactly you are trying to achieve.
But in general, if you already have a controller, that receives the data, then you can get them from the request like this:
$data = $request->request->all();
By this, you have all the values from your form saved in an array $data. You have written, that you already have an entity, so from that I assume, that your entity is already mapped to your database table. So the only thing you have to do, is to use the repository to save the data. For thta, you just need to inject it into your class and get a context. The context depends on where you currently are, so for the purpose of the example, I have just created the default context.
It should look like this:
class MyClass
{
protected $myEntityRepository;
public function __construct(
MyEntityRepository $myEntityRepository
)
{
$this->myEntityRepository = $myEntityRepository;
$this->context = \Shopware\Core\Framework\Context::createDefaultContext();
}
public function myMethod ($data)
{
$this->myEntityRepository->upsert($data, $this->context);
}
}
Hope this helps. I have actually written an article on repositories in Shopware 6, so if you want to get some more information and examples, you can check it here: https://shopwarian.com/repositories-in-shopware-6/.

Override Sails.js Waterline intercept Find method

I have a model named Product.
I want configurable across the board filtering for this model.
For example: sails.config.field = 2;
When I do GET /Product I want it to essentially do GET /Product?where={"field": 2}
The above works for blueprint by adding a policy, but I want consistent behavior when I use the waterline ORM
GET /Product
and Product.find() should return the same thing.
I can override the model: Product.find and it would work perfectly... IF there was some way for me to access the underlying find code.
The code I am using to intercept the blueprint is:
if (!req.query.where) {
req.query.where = `{"status":{">":0,">=":${sails.config.catalogVersions.status}}}`;
} else {
const parsedWhere = JSON.parse(req.query.where);
parsedWhere.status = {
'>': 0,
'>=': sails.config.catalogVersions.status,
};
req.query.where = JSON.stringify(parsedWhere);
}
I could very easily apply this to a Model.find interceptor.
Is there any way that once sails is loaded, I can access the root find method on a model even if its been overwritten at load time?
Maybe you could think on something like this one:
https://github.com/muhammadghazali/sails-hook-pagination
It's a hook and maybe works for your intended use.
I ended up using the hook:orm:loaded hook, to run some code which monkeypatched all of the models with a defaultScope which was stored in each of my models. It works well as I can easily modify the default criteria on all of the models and get consistent behavior across blueprint and waterline.
For code see:
Is there a way to override sails.js waterline endpoint with custom controller, but keep built in pagination, and filtering?

GXT (Ext-GWT) + Pagination + HTTP GET

I'm trying to populate a GXT Grid using data retrieved from an online API (for instance, going to www.example.com/documents returns a JSON array of documents). In addition, I need to paginate the result.
I've read all the various blogs and tutorials, but most of them populate the pagination proxy using something like TestData.GetDocuments(). However, I want to get that info using HTTP GET.
I've managed to populate a grid, but without pagination, using a RequestBuilder + proxy + reader + loader. But it seems as though the actual loading of the data is "put off" until some hidden stage deep inside the GXT code. Pagination requires that data from the start, so I'm not sure what to do.
Can someone provide a simple code example which does what I need?
Thank you.
I managed to get this going, here is what I did:
First I defined the proxy and loader for my data along with the paging toolbat:
private PagingModelMemoryProxy proxy;
private PagingLoader<PagingLoadResult<ModelData>> loader;
private PagingToolBar toolBar;
Next is the creation of each one, initializing with an empty ArrayList.
proxy = new PagingModelMemoryProxy(new ArrayList<EquipmentModel>());
loader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy);
loader.setRemoteSort(true);
toolBar = new PagingToolBar(100);
toolBar.bind(loader);
loader.load(0, 100);
Last, I have a set method in my view that gets called when the AJAX call is complete, but you could trigger it anywhere. Here is my entire set method, Equipment and EquipmentModel are my database and view models respectively.
public void setEquipmentData(List<Equipment> data)
{
Collections.sort(data);
// build a list of models to be loaded
List<EquipmentModel> models = new ArrayList<EquipmentModel>();
for (Equipment equipment : data)
{
EquipmentModel model = new EquipmentModel(equipment);
models.add(model);
}
// load the list of models into the proxy and reconfigure the grid to
// refresh display.
proxy.setData(models);
ListStore<EquipmentModel> equipmentStore = new ListStore<EquipmentModel>(loader);
equipmentGrid.reconfigure(equipmentStore, equipmentColumnModel);
loader.load(0, 100);
}
The key here for me was re-creating the store with the same loader, the column model was pre-created and gets reused.

ASP.NET MVC2 Posting a ViewModel mapping to Domain (LINQ) to Submitting changes

I'm using AutoMapper to map between a Linq Domain object and a ViewModel to display an Edit Form to the user which works perfectly.
When they click submit I'd like to know the best way to map the ViewModel back to a Linq entity and persist it to the database.
The Linq entity I'm using has multiple collections of other entities (ie one-to-many references).
I was trying:
build up my custom view model using UpdateModel
get the previous state of the Linq entity by using the passed in id
map the view model onto the Linq entity (using automapper)
call SubmitChanges() on the data context
This method works when I'm only updating properties that are not collections, but throws errors when trying to modify properties that are collections.
Any help/thoughts would be much appreciated :)
I've taken an approach that is very similar to that used by Jimmy Bogard in the CodeCampServer project (http://codecampserver.codeplex.com/)
I have a general Mapper class that I inherit from where I just need to override a MapToModel method that maps from the ViewModel to the domain Model, and a GetIdFromViewModel method that returns the proper Id form the ViewModel so that the Service layer can grab the domain model from the database.
I had to change a little from the CodeCampServer examples because some of my Models used Guid and some used int as the Id for the Model.
You should be able to grab the code from the codeplex link above and that should help get you going in that direction.
Here is what one of my Mappers for a Member looks like:
public class MemberMapper : AutoFormMapper<Member, MemberFormViewModel, Guid>, IMemberMapper
{
public MemberMapper(IMemberService service) : base(service) { }
protected override Guid GetIdFromViewModel(MemberFormViewModel viewModel)
{
return viewModel.MemberId;
}
protected override void MapToModel(MemberFormViewModel viewModel, Member model)
{
// if the need arises, we will need to map the full objects as Foreign Key properties
// by using the proper repositories
// right now for loading the object to save back to the DB we don't have that need, so let's not waste the call
model.MemberId = viewModel.MemberId;
model.FirstName = viewModel.FirstName;
model.LastName = viewModel.LastName;
model.Title = viewModel.Title;
model.EmailAddress = viewModel.EmailAddress;
model.DirectPhone = viewModel.DirectPhone;
model.MobilePhone = viewModel.MobilePhone;
model.ElectronicId = viewModel.ElectronicId;
model.ProjectRoleTypeId = viewModel.ProjectRoleTypeId;
model.DepartmentId = viewModel.DepartmentId;
}
}
Then you can use this MemberMapper to map both directions. It uses AutoMapper to go from the domain Model to the View Model and then uses the MapToModel method that you implement to map from the View Model back to the domain Model.