Laravel 5 Eloquent Condition With Pivot - eloquent

I have the Following models relationship.
promotions: [id, title, description]
sectors: [id, name]
promotion_sector: [promotion_id, sector_id]
class Promotion extends Model {
public function sectors() {
return $this->belongsToMany('App\Sector');
}
}
I want to get the promotions which are in given sectors.
For example,
All the promotions in sector A and B

To get all sectors associated with a promotion.
Try this:
class Promotion extends Model {
public function sectors() {
return $this->belongsToMany('App\Sector', 'promotion_sector', 'promotion_id', 'sector_id');
}
}
To verify, try this in artisan tinker:
$pro = App\Promotion::find(1);
$pro->sectors;
$pro;
You will get the list of all sectors associated with Promotion with Id 1.
To do the opposite, that you have asked in the question.
You need to do this:
class Sector extends Model {
public function promotions() {
return $this->belongsToMany('App\Promotion', 'promotion_sector', 'sector_id', 'promotion_id');
}
}
To verify, try this in artisan tinker:
$sec = App\Sector::find(1);
$sec->promotions;
$sec;
You will get the list of all the promotions associated with sector with Id 1.

I don't know which type of relationship you are using. I presume it to be Many-To-Many Relationship.
So here's the code you can try:
$result = DB::table('promotion_sector')
->join('promotions', 'id', '=', 'promotion_sector.promotion_id')
->join('sectors', 'id', '=', 'promotion_sector.sector_id')
->select('sectors.name AS sector_name')
->get();
dd( $result );
And if you are asking for the input from the user:
$result = DB::table('promotion_sector')
->join('promotions', 'id', '=', 'promotion_sector.promotion_id')
->join('sectors', 'id', '=', 'promotion_sector.sector_id')
->where( 'sectors.name', '=', $request->input('name_of_the_field') )
->select('sectors.name AS sector_name')
->get();
dd( $result );

Related

Codeigniter Rest Api Delete using parameter ID

I'm Learning CRUD operation with Codeigniter and Rest Api for my app Flutter.But I'm get some problem with Delete operation. I'm want delete data using parameter ID , but the message show me ID Null.
It's my Get Operation using parameter
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/get?id=5
Possible i'm make delete operation like this ?
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/delete?id=5
I already try
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/delete/5
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/delete/id/5
But Nothing change , still ID null.
It's my Controller And Model Rest Api :
Controller
public function delete_delete()
{
$id = $this->delete('id');
$msgDelete = ['id' => $id, 'message' => 'Deleted the resource'];
$msgEmpty = ['status' => false, 'message' => 'ID Not Found'];
$msgBadRequest = ['status' => false, 'message' => 'Provide an ID'];
if ($id === null) {
$this->set_response($msgBadRequest, 400);
} else {
if ($this->mahasiswa->deleteMahasiswa($id) > 0) {
$this->set_response($msgDelete, 204);
} else {
$this->set_response($msgEmpty, 404);
}
}
}
Model
public function deleteMahasiswa($id)
{
$this->db->delete('mahasiswa', ['id' => $id]);
return $this->db->affected_rows();
}
I am seeing some ambigius things in your code for example the api url is :
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/delete/5
and your controller method name is delete_delete .
If you are using the get method you should respect the following :
base_url/controller_name/method_name?id=3
and then you can get the value with :
$this->input->get('id')
otherwise if you are passing the id as a method argument like the following :
http://your_domain/controller_name/delete/5
you can get the value like this :
class controller_name extends CI_Controller {
.
.
.
public function delete($id=null){
}
}
If Possible do like this
192.168.43.159/wpu-rest-server/apii/mahasiswa/delete_delete?id=5
Controller
public function delete_delete() {
$id = $this->input->get('id');
$this->mahasiswa->deleteMahasiswa($id);
redirect('http://192.168.43.159/wpu-rest-server/apii/mahasiswa');
}
Model
function deleteMahasiswa() {
$this->db->where('id', $id);
$this->db->delete('mahasiswa');
}

How can I refine a REST query based on a linked module?

I'm trying to get all accounts that do not have an associated document.
This is what I have in mind:
$parameters = array(
'session' => $this->getSessionId(),
'module_name' => 'Accounts',
'query' => "accounts.id NOT IN (SELECT DISTINCT account_id FROM documents_accounts)",
);
but of course it doesn't work. I get an "Access denied" error (40).
Ideally I'd like to filter even further by querying for accounts that do not have an attached document of a given type. For example:
getAccountsWithout('contracts');
getAccountsWithout('quote1');
...
It could be that your are not correctly authenticating with the REST API. Or that your are not correctly provide what the method call needs.
If that is not the issue. One solution is to create a customer version the service/v4_1. So that you can add your own custom call.
This can be achieved by copying the v4_1 directory giving it a name like v4_1_1.
Rename the following:
SugarWebServiceImplv4_1.php to SugarWebServiceImplv4_1_1.php
SugarWebServiceUtilv4_1.php to SugarWebServiceImplv4_1_1.php
Change the class signatures to this:
SugarWebServiceImplv4_1:
class SugarWebServiceImplv4_1_1 extends SugarWebServiceImplv4_1
SugarWebServiceImplv4_1:
class SugarWebServiceUtilv4_1_1 extends SugarWebServiceUtilv4_1
Change the require/include paths in:
registry.php rest.php
soap.php
SugarWebServiceImplv4_1_1.php
SugarWebServiceImplv4_1_1.php
so that they now point to the 4_1_1 directory.
Implement a method in SugarWebServiceImplv4_1_1.php.
function get_without_documents($session, $module_name = 'accounts', $start = 1, $offset = 20) {
$output_list = array();
$db = DBManagerFactory::getInstance();
// just for example
$sql_query = "SELECT a.id FROM accounts a WHERE a.id NOT IN (SELECT DISTINCT account_id FROM documents_accounts)";
$result = $db->limitQuery($sql_query, $start, $offset,false);
while(($row = $db->fetchByAssoc($result)) != null){
$output_list[] = $row['id'];
}
return array(
'result_count'=> count($output_list),
'next_offset' => $offset + $start,
'entry_list' => $output_list,
);
}
Finally register the method and it the types in registry.php and the reference the url so that it uses v4_1_1.

Yii CJuiAutoComplete for Multiple values

I am a Yii Beginner and I am currently working on a Tagging system where I have 3 tables:
Issue (id,content,create_d,...etc)
Tag (id,tag)
Issue_tag_map (id,tag_id_fk,issue_id_fk)
In my /Views/Issue/_form I have added a MultiComplete Extension to retrieve multiple tag ids and labels,
I have used an afterSave function in order to directly store the Issue_id and the autocompleted Tag_ids in the Issue_tag_map table, where it is a HAS_MANY relation.
Unfortunately Nothing is being returned.
I wondered if there might be a way to store the autocompleted Tag_ids in a temporary attribute and then pass it to the model's afterSave() function.
I have been searching for a while, and this has been driving me crazy because I feel I have missed a very simple step!
Any Help or advices of any kind are deeply appreciated!
MultiComplete in Views/Issue/_form:
<?php
echo $form->labelEx($model, 'Tag');
$this->widget('application.extension.MultiComplete', array(
'model' => $model,
'attribute' => '', //Was thinking of creating a temporary here
'name' => 'tag_autocomplete',
'splitter' => ',',
'sourceUrl' => $this->createUrl('Issue/tagAutoComplete'),
// Controller/Action path for action we created in step 4.
// additional javascript options for the autocomplete plugin
'options' => array(
'minLength' => '2',
),
'htmlOptions' => array(
'style' => 'height:20px;',
),
));
echo $form->error($model, 'issue_comment_id_fk');
?>
AfterSave in /model/Issue:
protected function afterSave() {
parent::afterSave();
$issue_id = Yii::app()->db->getLastInsertID();
$tag; //here I would explode the attribute retrieved by the view form
// an SQL with two placeholders ":issue_id" and ":tag_id"
if (is_array($tag))
foreach ($tag as $tag_id) {
$sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";
$command = Yii::app()->db->createCommand($sql);
// replace the placeholder ":issue_id" with the actual issue value
$command->bindValue(":issue_id", $issue_id, PDO::PARAM_STR);
// replace the placeholder ":tag_id" with the actual tag_id value
$command->bindValue(":tag_id", $tag_id, PDO::PARAM_STR);
$command->execute();
}
}
And this is the Auto Complete sourceUrl in the Issue model for populating the tags:
public static function tagAutoComplete($name = '') {
$sql = 'SELECT id ,tag AS label FROM tag WHERE tag LIKE :tag';
$name = $name . '%';
return Yii::app()->db->createCommand($sql)->queryAll(true, array(':tag' => $name));
actionTagAutoComplete in /controllers/IssueController:
// This function will echo a JSON object
// of this format:
// [{id:id, name: 'name'}]
function actionTagAutocomplete() {
$term = trim($_GET['term']);
if ($term != '') {
$tags = issue::tagAutoComplete($term);
echo CJSON::encode($tags);
Yii::app()->end();
}
}
EDIT
Widget in form:
<div class="row" id="checks" >
<?php
echo $form->labelEx($model, 'company',array('title'=>'File Company Distrubution; Companies can be edited by Admins'));
?>
<?php
$this->widget('application.extension.MultiComplete', array(
'model' => $model,
'attribute' => 'company',
'splitter' => ',',
'name' => 'company_autocomplete',
'sourceUrl' => $this->createUrl('becomEn/CompanyAutocomplete'),
'options' => array(
'minLength' => '1',
),
'htmlOptions' => array(
'style' => 'height:20px;',
'size' => '45',
),
));
echo $form->error($model, 'company');
?>
</div>
Update function:
$model = $this->loadModel($id);
.....
if (isset($_POST['News'])) {
$model->attributes = $_POST['News'];
$model->companies = $this->getRecordsFromAutocompleteString($_POST['News']
['company']);
......
......
getRecordsFromAutocompleteString():
public static cordsFromAutocompleteString($string) {
$string = trim($string);
$stringArray = explode(", ", $string);
$stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
$criteria = new CDbCriteria();
$criteria->select = 'id';
$criteria->condition = 'company =:company';
$companies = array();
foreach ($stringArray as $company) {
$criteria->params = array(':company' => $company);
$companies[] = Company::model()->find($criteria);
}
return $companies;
}
UPDATE
since the "value" porperty is not implemented properly in this extension I referred to extending this function to the model:
public function afterFind() {
//tag is the attribute used in form
$this->tag = $this->getAllTagNames();
parent::afterFind();
}
You should have a relation between Issue and Tags defined in both Issue and Tag models ( should be a many_many relation).
So in IssueController when you send the data to create or update the model Issue, you'll get the related tags (in my case I get a string like 'bug, problem, ...').
Then you need to parse this string in your controller, get the corresponding models and assigned them to the related tags.
Here's a generic example:
//In the controller's method where you add/update the record
$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');
Here the method I'm calling:
//parse your string ang fetch the related models
public static function getRecordsFromAutocompleteString($string, $model, $field)
{
$string = trim($string);
$stringArray = explode(", ", $string);
$stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
return CActiveRecord::model($model)->findAllByAttributes(array($field => $stringArray));
}
So now your $issue->tags is an array containing all the related Tags object.
In your afterSave method you'll be able to do:
protected function afterSave() {
parent::afterSave();
//$issue_id = Yii::app()->db->getLastInsertID(); Don't need it, yii is already doing it
foreach ($this->tags as $tag) {
$sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";
$command = Yii::app()->db->createCommand($sql);
$command->bindValue(":issue_id", $this->id, PDO::PARAM_INT);
$command->bindValue(":tag_id", $tag->id, PDO::PARAM_INT);
$command->execute();
}
}
Now the above code is a basic solution. I encourage you to use activerecord-relation-behavior's extension to save the related model.
Using this extension you won't have to define anything in the afterSave method, you'll simply have to do:
$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');
$issue->save(); // all the related models are saved by the extension, no afterSave defined!
Then you can optimize the script by fetching the id along with the tag in your autocomplete and store the selected id's in a Json array. This way you won't have to perform the sql query getRecordsFromAutocompleteString to obtain the ids. With the extension mentioned above you'll be able to do:
$issue->tags = CJSON::Decode($_POST['idTags']);//will obtain array(1, 13, ...)
$issue->save(); // all the related models are saved by the extension, the extension is handling both models and array for the relation!
Edit:
If you want to fill the autocomplete field you could define the following function:
public static function appendModelstoString($models, $fieldName)
{
$list = array();
foreach($models as $model)
{
$list[] = $model->$fieldName;
}
return implode(', ', $list);
}
You give the name of the field (in your case tag) and the list of related models and it will generate the appropriate string. Then you pass the string to the view and put it as the default value of your autocomplete field.
Answer to your edit:
In your controller you say that the companies of this model are the one that you added from the Autocomplete form:
$model->companies = $this->getRecordsFromAutocompleteString($_POST['News']
['company']);
So if the related company is not in the form it won't be saved as a related model.
You have 2 solutions:
Each time you put the already existing related model in you autocomplete field in the form before displaying it so they will be saved again as a related model and it won't disapear from the related models
$this->widget('application.extensions.multicomplete.MultiComplete', array(
'name' => 'people',
'value' => (isset($people))?$people:'',
'sourceUrl' => array('searchAutocompletePeople'),
));
In your controller before calling the getRecordsFromAutocompleteString you add the already existing models of the model.
$model->companies = array_merge(
$model->companies,
$this->getRecordsFromAutocompleteString($_POST['News']['company'])
);

How does one create a validator that depends on more than one value for Zend_Form?

I've got a form that has a single text field (for company):
class Cas_Form_Company extends Zend_Form
{
public function init()
{
$this->addElement('hidden', 'id');
$this->addElement('text', 'name', array('label' => 'Name'));
$this->addElement('submit', 'submit', array('label' => 'Create'));
$name = $this->getElement('name');
$name->addValidator('stringLength', false, array(2,45));
$name->addValidator(new Cas_Model_Validate_CompanyUnique());
$this->setMethod('post');
$this->setAction(Zend_Controller_Front::getInstance()->getBaseUrl() . '/Company/Submit');
}
public function SetFromExistingCompany(Cas_Model_Company $company)
{
$this->getElement('id')->setValue($company->GetId());
$this->getElement('name')->setValue($company->GetName());
$this->getElement('submit')->setLabel('Edit');
$this->addElement('submit', 'delete', array('label' => 'Delete', 'value' => 'delete'));
}
public function Commit()
{
if (!$this->valid())
{
throw new Exception('Company form is not valid.');
}
$data = $this->getValues();
if (empty($data['id']))
{
Cas_Model_Gateway_Company::FindOrCreateByName($data['name']);
}
else
{
$company = Cas_Model_Gateway_Company::FindById((int)$data['id']);
$company->SetName($data['name']);
Cas_Model_Gateway_Company::Commit($company);
}
}
}
I've also created a little validator which enforces that I want companies to have unique names:
class Cas_Model_Validate_CompanyUnique extends Zend_Validate_Abstract
{
protected $_messageTemplates = array(
'exists' => '\'%value%\' is already a company.'
);
/**
* #param string $value
* #return bool
*/
public function isValid($value)
{
$this->_setValue($value);
$company = Cas_Model_Gateway_Company::FindByName($value);
if ($company)
{
$this->_error('exists');
return false;
}
return true;
}
}
Now, this works just fine for creating new companies. The problem comes in when I want to allow editing of companies. This is because for an edit operation, while the company name needs to be unique, a form containing the name already pertaining to the given ID isn't an edit at all (and therefore is valid). That is, the form is valid if either the name doesn't already exist in the database, or the name given matches the name already assigned to that ID.
However, writing this as a validator seems to be problematic, because the validator only gets the value it's working on -- not the ID in question.
How does one write a validator for this sort of thing?
You can use the poorly documented second $context argument to isValid().
See http://framework.zend.com/manual/en/zend.form.elements.html#zend.form.elements.validators and scroll down to the note "Validation Context"
I think this link may help you.
Zend Form Edit and Zend_Validate_Db_NoRecordExists
You have to user Db no record exist but for edit you can specify the exclude attribute in validation.

$_referenceMap Relationships with Zend_Db_Table_Abstract creates too many queries

This is my first time using Zend Framework for an application and i don't know if I completely have by head around Models.
I have four tables: shopping_cart, product, product_unit, distributor.
shopping cart has an cart_id, product_id, unit_id and dist_id (shopping cart joins on the other tables with their corresponding id).
Before Zend I would create a class like this:
class ShoppingCart
{
function getItems()
{
$sql ="select * from shopping_cart, product, product_unit, distributor
where
shopping_cart.product_id = product.id AND
shopping_cart.unit_id = product_unit.id AND
shopping_cart.dist_id = distributor.id AND
cart_id = xxx";
$items = $this->db->getAll($sql);
}
One query to get all the information from the joined tables.
When I set up the relationship mapping in Zend_Db_Table_Abstract:
My Shopping Cart Model:
class Application_Model_ShoppingCart
{
function __construct()
{
$this->ShoppingCartTable = new Application_Model_DbTable_ShoppingCart();
}
function getItems()
{
$cart_items = $this->ShoppingCartTable->getItems($this->GetCartId());
return $cart_items;
}
}
class Application_Model_DbTable_ShoppingCart extends Zend_Db_Table_Abstract
{
protected $_name = 'shopping_cart';
protected $_rowClass = 'Application_Model_DbTable_ShoppingCart_Item';
protected $_referenceMap = array(
'Product' => array(
'columns' => 'product_id',
'refTableClass' => 'Application_Model_DbTable_Product',
'refColumns' => 'id'
),
'Distributor' => array(
'columns' => 'dist_id',
'refTableClass' => 'Application_Model_DbTable_Distributor',
'refColumns' => 'id'
),
'Unit' => array(
'columns' => 'unit_id',
'refTableClass' => 'Application_Model_DbTable_ProductUnit',
'refColumns' => 'id'
)
);
public function getItems($cart_id)
{
$where = $this->getAdapter()->quoteInto('cart_id = ?', $cart_id);
return $this->fetchAll($where);
}
}
In my controller:
$this->_shoppingCartModel = new Application_Model_ShoppingCart();
$items = $this->_shoppingCartModel->getItems();
IN my view :
foreach($this->items AS $item)
{
$item_product = $item->findParentRow('Application_Model_DbTable_Product');
$item_dist = $item->findParentRow('Application_Model_DbTable_Distributor');
$item_unit = $item->findParentRow('Application_Model_DbTable_ProductUnit');
}
when I have ten items in my cart the db profiler shows over sixty queries (WHOA) to view the cart items ( information across all four tables are displayed - product name, unit description, distributor name).
For each item it queries the shopping_cart, then querys the product table, then the product unit, then the distributor_table.
Is there a way to have this run as one query joining all the tables via Zend_Db_Table_Abstract relationships?
Will I have to go back to using db adapter in the my Application_Model_ShoppingCart class?
I want to abstract all data access to the table models (Application_Model_DbTable_ShoppingCart) and not have the Application_Model_ShoppingCart tied to a db handler.
Thanks in advance for advice, I love the Zend Framework but models are still hard for me to understand given the different conflicting ways people talk about using them.
In short, no, unfortunately it's not possible to get a set of table rows together with their relationships in a single query.
It's just that all methods dealing with relationships are defined for a row, not for a table.
But at least, you can form your sql with Zend_Db_Table_Select instead of writing it all manually.
Upd: Your code for fetching ShoppingCarts, in my opinion, should belong to the table (DbTable_ShoppingCart). So, the code you provided in the beginning could be transformed to the following:
class Application_Model_DbTable_ShoppingCart extends Zend_Db_Table_Abstract {
public function getItem($cart_id) {
$select = $this->select()
->from( array('sc' => 'shopping_cart'), array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('p' => 'product'), 'sp.product_id = p.id', array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('pu' => 'product_unit'), 'sp.unit_id = pu.id', array(Zend_Db_Select::SQL_WILDCARD) )
->join( array('d' => 'distributor'), 'sp.dist_id = d.id', array(Zend_Db_Select::SQL_WILDCARD) )
->where('sp.cart_id = ?', $cart_id)
->setIntegrityCheck(false);
return $this->fetchAll($select);
}
}