laravel updateOrCreate() with dynamic table - laravel-5.1

I'm working with project where tables are created all the time depending on user adding new properties, so for my model i have below
class Hotel extends Model
{
public function __construct($hotel)
{
$this->table = $hotel;
}
protected $fillable = ['date', 'sold', 'sold_diff', 'rev', 'rev_diff', 'row', 'date_col', 'sold_col', 'rev_col'];
}
and i can use the table in controller by doing
$hotel_table = new Hotel($table);
but i like to use Model::updateOrCreate() when I'm adding or updating rows in table, and I'm not sure how to do that.

This is the signature for the updateOrCreate function
"static Model updateOrCreate( array $attributes, array $values = array())"
For you to update or create, you can pass the condition that must be met to update the table to the first argument and the values to be updated to the second.
for example.
$primaryKey = isset($request->input('id')) ? $request->input('id') : null;
$myModel = Model::updateOrCreate(['id' => $primaryKey], $request->all());
so with this if id is in the request object the table will be updated but if not a new record will be created.

In Laravel 5.2 you can use simply like this
class Hotel extends Model
{
protect $table = 'hotels'
protected $fillable = ['date', 'sold', 'sold_diff', 'rev', 'rev_diff', 'row', 'date_col', 'sold_col', 'rev_col'];
// protected $guarded = []; you can use this instead of `$fillable` this is for all columns fillable
}
now in your controller you can use
Model::update();
Model::create();

Related

Laravel, how to update two tables with a one-to-one relationship from a single form

I have these two very simple models:
class Episode extends Model
{
protected $fillable = ['number', 'language_code', 'published_at'];
function content()
{
return $this->hasOne('App\EpisodeContent');
}
}
class EpisodeContent extends Model
{
protected $table = 'episodes_content';
protected $fillable = ['episode_id', 'title', 'description'];
function episode()
{
return $this->belongsTo('App\Episode');
}
}
Where basically every Episode has one content. I could've used a single table, but I thought it could make sense to keep these sets of data separate.
In a form, I'd like to edit the Episode and its content at the same time, but after several attempts I haven't figured out how.
This is what I'm doing:
public function update(Request $request, $id)
{
$rules = [
'number' => 'required',
];
$this->validate($request, $rules);
$episode = Episode::with('content')->findOrFail($id);
$episode->published_at = $request->get('published_at');
$episode->number = $request->get('number');
$episode->content->title = $request->get('title');
$episode->update();
return redirect('admin/episodes');
}
This way, nothing changes in the episodes_content table.
In another attempt, I tried this:
$episode->published_at = $request->get('published_at');
$episode->number = $request->get('number');
$episode->active = $request->get('active');
$episodeContent = new EpisodeContent;
$episodeContent->title = $request->get('title');
$episode->content()->save($episodeContent);
This way, a new episodes_content row is created, while I'd like to update an existing one.
Try this I guess it will work
$episode = Episode::with('content',function($query) use($id){
$query->where('episode_id',$id);
})->where('id',$id);
Here I'm assuming your field name is id in parent table and episode_id in child table. Let me know if it doesn't work.

Save/retrieve data by foreign key (API)

I'm attempting to set up my API for iOS app. This is my first time to use Laravel as a API so here is what I have in my tables:
Cars
Schema::create('cars', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('age')->nullable();
$table->string('model')->nullable();
$table->string('color')->nullable();
Users
$table->increments('id');
$table->string('name')->nullable();
$table->string('street')->nullable();
$table->string('niehgborhood')->nullable();
$table->string('city')->nullable();
Contract
$table->increments('id');
$table->integer('user_id')->unsigned;
$table->foreign('user_id')->references('id')->on('users');
$table->integer('car_id')->unsigned;
$table->foreign('car_id')->references('id')->on('cars');
Models
protected $table = 'users';
protected $guarded = ['id'];
protected $fillable = ['phone', 'email',
'street','city','niehgborhood'
,'national_id'];
public function cars()
{
return $this->hasMany('App\User');
}
Users
protected $guarded = ['id'];
protected $fillable = ['name', 'age',
'color, model'
];
public function users()
{
return $this->belongsToMany('App\Cars');
}
in my controller I'm familiar with saving requested data
$user = JWTAuth::parseToken()->authenticate();
$user->phone = $request->phone;
$user->city = $request->city;
$user->save();
my goal with this project is to display the data(contracts) saved by iOS app users in my dashboard. For example, users info and the cars they are interested in my table. Can someone help me with what to do query in the controller(not views). Or provide helpful links for projects like this.
Your relation between User and Cars should be many-to-many. Please read the docs to apply this relation properly.
If your relations are in the place then you can do as:
$user = JWTAuth::parseToken()->authenticate();
$cars = $user->cars; // returns collection of cars associated with the user.
For example - In your User model define the following relation:
public function cars()
{
return $this->belongsToMany('App\Car');
}
Update
To save the user and associate cars, you can do it as following:
$user = JWTAuth::parseToken()->authenticate();
$user->phone = $request->phone;
$user->city = $request->city;
$user->save();
$car_ids = $request->car_ids; // it should return an array
$user->cars()->sync($car_ids);
There is more way to store the data. Please read the docs here for more info.

Zend\Db Model with Child Models

ZF2 project - no Doctrine, using native Zend\Db: Have the following structure:
Controller
ProductController
Model
Product
ProductTable
ProductType
ProductTypeTable
Product is the model, has variables corresponding to the “products" table fields.
ProductTable is table class which is connected to the database via tableGateway. ProductTable has getItem() method to retrieve requested product by “id”.
ProductType is the model, has variables like id, name, description corresponding to the “productTypes" table fields.
ProductTypeTable is table class just like ProductTable.
Each product belongs to a certain ProductType
products.productTypeId = productTypes.id
is the relation.
In ProductTable->getItem() method, I can simply get productTypeId.
I can use joins to get productTypes.name, productTypes.description, or any field from "productTypes" table.
But I don’t want to do this - instead dealing with new variables in Product entity like productTypeName, productTypeDesc,
I’d like to have Product->getProductType() and set it to be a ProductType object, so I can get Product->getProductType() ->getName() to get product type name.
Simply I’d like to assign a child model as a variable of the parent model.
I can do this in the controller like below:
$product = $this->getProductTable()->getItem(7); // id = 7
$product->setProductType($this->getProductTypeTable()
->getItem($product->getProductTypeId());
But I’d like to make it happen in product table class getItem() method. So I don’t have to think about it in every controller, and it is kind of encapsulated.
What is the right way to do this?
Thank you.
The issue that you have is the Table Gateway pattern is only really any good at abstracting database access to a a single database table. It does not in anyway allow for the hydration of entities or management of relationships. Object Relationship Mappers (ORM's), such as Doctrine, solve this problem.
If Doctrine, for whatever reason, is inappropriate for your use case an alternative could be implementing the Data Mapper Pattern
The Data Mapper is a layer of software that separates the in-memory objects from the database. Its responsibility is to transfer data between the two and also to isolate them from each other
The data mapper will use the table gateway to fetch the required data for each table and construct the Product instance, including it's associated ProductType. You would then expose the mapper to the controller (rather than the table gateway).
A simple example of a ProductMapper.
class ProductMapper
{
// #var \Zend\Db\TableGateway\TableGateway
protected $productTable;
protected $productTypeMapper;
// an 'identity map' of loaded products
protected $loaded = [];
public function __construct(ProductTable $productTable, ProductTypeMapper $productTypeMapper)
{
$this->productTable = $productTable;
$this->productTypeMapper = $productTypeMapper;
}
protected function hydrate(Product $product, array $data)
{
$product->setId($data['id']);
$product->setName($data['name']);
$product->setFoo($data['foo']);
if (isset($data['type_id'])) {
// Load a fully constructed product type from the database
$type = $this->productTypeMapper->findById($data['type_id']);
$product->setType($type);
}
return $product;
}
public function findById($id)
{
if (isset($this->loaded[$id])) {
return $this->loaded[$id];
}
// Get the data
$row = $this->productTable->select(['id' => $id]);
if (empty($row)) {
throw new SomeCustomException("No product could be found with id $id");
}
// Create and hydrate the product
$product = $this->hydrate(new Product, $row->current())
$this->loaded[$id] = $product;
return $product;
}
public function save(array $data);
public function update($data);
public function delete($id);
}
You can achieve this, you just have to follow the following 3 steps:
Make your Product->exchangeArray() function smarter
Get all required ProductType fields, using a prefix helps for example: type_
Add #var ProductType so you will have proper autocompete (works for me in Eclipse)
<?php
namespace Product\Model\Product;
class Product {
public $id;
...
/**
* #var ProductType
*/
public $productType;
...
public function exchangeArray( $data ) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
...
$productType = new ProductType();
$typeData = array(
'id' => $data['type_id'],
'value' => $data['type_value']
);
$productType->exchangeArray( $typeData );
$this->productType = $productType;
}
}

Get and set the current value from the relationship between "maquina" and "emisor"

Related to this topic and this other topic I'm experimenting a issue. This is the SdrivingMaquinForm.class.php code:
class SdrivingMaquinaForm extends BaseSdrivingMaquinaForm {
protected $current_user;
public function configure() {
$this->current_user = sfContext::getInstance()->getUser()->getGuardUser();
unset($this['updated_at'], $this['created_at']);
$this->widgetSchema['idempresa'] = new sfWidgetFormInputHidden();
$id_empresa = $this->current_user->getSfGuardUserProfile()->getIdempresa();
$this->setDefault('idempresa', $id_empresa);
$this->widgetSchema['no_emisor'] = new sfWidgetFormDoctrineChoice(array('model' => 'SdrivingEmisor', 'add_empty' => 'Seleccione un Emisor', 'table_method' => 'fillChoice'));
$this->validatorSchema['idempresa'] = new sfValidatorPass();
$this->validatorSchema['no_emisor'] = new sfValidatorPass();
}
protected function doUpdateObject($values) {
parent::doUpdateObject($values);
if (isset($this['no_emisor'])) {
if ($this->isNew()) {
$sdrivingMaquinaEmisor = new SdrivingMaquinaEmisor();
$this->getObject()->setSdrivingMaquinaEmisor($sdrivingMaquinaEmisor);
} else {
$sdrivingMaquinaEmisor = $this->getObject()->getSdrivingMaquinaEmisor();
}
$sdrivingMaquinaEmisor->setIdemisor($this->values['no_emisor']);
}
}
}
And it works perfectly, if I create a new maquina values are saved correctly, if I edit a existent record once again values are saved correctly and if I delete a record then the relation is deleted too. So the problem is not in actions or method. The problem I'm having is when user select to edit the existent record. Field idempresa and patente (see the schema.yml at first post metioned here) gets theirs values but no_emisor doesn't so every time I want to edit the record I got the select with values, yes, but the selected value isn't the right because I get the add_empty value. How I fix that? Meaning how I assign the default value for the select based on the one existent on the relation between maquina and emisor?
EDIT: working on a possible solution
I'm trying this code:
public function executeEdit(sfWebRequest $request) {
$this->forward404Unless($sdriving_maquina = Doctrine_Core::getTable('SdrivingMaquina')->find(array($request->getParameter('idmaquina'))), sprintf('Object sdriving_maquina does not exist (%s).', $request->getParameter('idmaquina')));
$this->forward404Unless($sdriving_maquina_emisor = Doctrine_Core::getTable('SdrivingMaquinaEmisor')->find(array($request->getParameter('idmaquina'))), sprintf('Object sdriving_maquina_emisor does not exist (%s).', $request->getParameter('idmaquina')));
$this->form = new SdrivingMaquinaForm($sdriving_maquina, $sdriving_maquina_emisor);
}
But then how in the form configure() method I can access to $sdriving_maquina_emisor in order to use form setDefault() method?
EDIT: doUpdateObject($values)
See this is how my doUpdateObject($values) function looks like:
protected function doUpdateObject($values) {
parent::doUpdateObject($values);
if (isset($this['no_emisor'])) {
if ($this->isNew()) {
$sdrivingMaquinaEmisor = new SdrivingMaquinaEmisor();
$this->getObject()->setSdrivingMaquinaEmisor($sdrivingMaquinaEmisor);
} else {
$sdrivingMaquinaEmisor = $this->getObject()->getSdrivingMaquinaEmisor();
}
$sdrivingMaquinaEmisor->setIdemisor($this->values['no_emisor']);
}
}
Where exactly feet the code you leave for doUpdateObject()?
In these situations you always have to do 2 things:
load the default value from the doctrine record object into the form widget
update the doctrine object with the posted value
And most of the times you should use updateDefaultsFromObject and doUpdateObject symmetrically.
To load back the saved values override updateDefaultsFromObject:
// maybe you have to declare it as public if the parent class requires that
protected function updateDefaultsFromObject()
{
parent::updateDefaultsFromObject();
if (isset($this['no_emisor'])
{
$this->setDefault('no_emisor', $this->getObject()->getSdrivingMaquinaEmisor()->getIdemisor());
}
}
// and you can simplify this a little bit as well
protected function doUpdateObject($values)
{
parent::doUpdateObject($values);
if (isset($this['no_emisor']))
{
$this->getObject()->getSdrivingMaquinaEmisor()->setIdemisor($this->values['no_emisor']);
}
}

return table primary key name with tableGateway

I'm trying to create an abstract object for my Table Objects.
Today I have lots of object like: CategoriaTable, FornecedoresTable, etc that implement $this->tableGateway->insert(), $this->tableGateway->update(), etc
I created an TableAbstract that contains most of those functionallities, but I stuck on one problem:
// In CategoriaTable my table id is named cat_id
$this->tableGateway->update($object->getArrayCopy(),array('cat_id' => $object->getId()))
// But in FornecedoresTable my table id is named for_id
$this->tableGateway->update($object->getArrayCopy(),array('for_id' => $object->getId()))
How can I get from tableGateway the id of an table? There is an better way to do what I want?
I guess I could inject the id name in my object but I don't thing this is a good way to do that...
You can create new TableGateway class parameter.(In my case I created $this->primary;)
And if it is not set use Zend\Db\Metadata\Metadata to find it straight from db structure.
<?php
//...
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Metadata\Metadata;
class AbstractTable extends AbstractTableGateway
{
protected $primary;
public function getPrimary()
{
if (null === $this->primary) {
$metadata = new Metadata($this->adapter);
$constraints = $metadata->getTable($this->getTable()->getTable())
->getConstraints();
foreach ($constraints AS $constraint) {
if ($constraint->isPrimaryKey()) {
$primaryColumns = $constraint->getColumns();
$this->primary = $primaryColumns;
}
}
}
return $this->primary;
}
}
?>

Resources